├── demo-album ├── app.js ├── pages │ ├── album │ │ ├── album.json │ │ ├── album.wxml │ │ ├── album.wxss │ │ └── album.js │ ├── preview │ │ ├── preview.json │ │ ├── preview.wxml │ │ ├── preview.wxss │ │ └── preview.js │ └── index │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.js ├── lib │ ├── api.js │ ├── request.js │ ├── cos.js │ └── util.js ├── images │ ├── wave.png │ └── camera.png ├── app.wxss ├── config.js ├── sitemap.json ├── .gitignore ├── app.json └── project.config.json ├── demo ├── pages │ └── index │ │ ├── index.json │ │ ├── index.wxss │ │ ├── index.wxml │ │ └── index.js ├── config.js ├── sitemap.json ├── app.wxss ├── app.json ├── demo-ci.js ├── app.js ├── project.config.json ├── ciDemo │ ├── mediaProcess.js │ ├── other.js │ ├── audit.js │ ├── asr.js │ ├── docPreview.js │ └── fileProcess.js ├── demo-post-policy.js ├── tools.js ├── lib │ ├── cos-auth.js │ └── beacon_mp.min.js └── demo-sdk.js ├── .babelrc ├── index.js ├── .gitignore ├── .npmignore ├── .prettierrc ├── scripts └── patch-check.js ├── patches └── fast-xml-parser+4.5.0.patch ├── src ├── event.js ├── async.js ├── session.js ├── cos.js ├── task.js └── tracker.js ├── LICENSE ├── package.json ├── .github └── workflows │ └── auto-changelog.yml ├── README.md ├── lib ├── request.js ├── base64.js └── crypto.js └── server └── sts.js /demo-album/app.js: -------------------------------------------------------------------------------- 1 | App({}); -------------------------------------------------------------------------------- /demo/pages/index/index.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /demo-album/pages/album/album.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /demo-album/lib/api.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | }; -------------------------------------------------------------------------------- /demo-album/pages/preview/preview.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var COS = require('./src/cos'); 2 | module.exports = COS; -------------------------------------------------------------------------------- /demo-album/images/wave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tencentyun/cos-wx-sdk-v5/HEAD/demo-album/images/wave.png -------------------------------------------------------------------------------- /demo-album/images/camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tencentyun/cos-wx-sdk-v5/HEAD/demo-album/images/camera.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .idea 3 | node_modules 4 | package-lock.json 5 | .DS_Store 6 | */.wechatide 7 | project.private.config.json 8 | -------------------------------------------------------------------------------- /demo-album/app.wxss: -------------------------------------------------------------------------------- 1 | page { 2 | background-color: #fff; 3 | } 4 | .main { 5 | position: relative; 6 | z-index: 2; 7 | margin: 40rpx 80rpx; 8 | } -------------------------------------------------------------------------------- /demo/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stsUrl: 'https://example.com/sts.php', 3 | Bucket: 'test-1250000000', 4 | Region: 'ap-guangzhou', 5 | }; 6 | -------------------------------------------------------------------------------- /demo-album/pages/index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarBackgroundColor": "#2277da", 3 | "navigationBarTextStyle": "white", 4 | "backgroundColor": "white" 5 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | .github/ 4 | coverage/ 5 | demo/ 6 | demo-album/ 7 | server/ 8 | patches/ 9 | .babelrc 10 | .prettierrc 11 | build.js 12 | CHANGELOG.md -------------------------------------------------------------------------------- /demo-album/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stsUrl: 'https://example.com/sts.php', 3 | Bucket: 'test-1250000000', 4 | Region: 'ap-guangzhou', 5 | albumDir: 'album/', 6 | }; -------------------------------------------------------------------------------- /demo/sitemap.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", 3 | "rules": [{ 4 | "action": "allow", 5 | "page": "*" 6 | }] 7 | } -------------------------------------------------------------------------------- /demo-album/sitemap.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", 3 | "rules": [{ 4 | "action": "allow", 5 | "page": "*" 6 | }] 7 | } -------------------------------------------------------------------------------- /demo-album/.gitignore: -------------------------------------------------------------------------------- 1 | # Windows 2 | [Dd]esktop.ini 3 | Thumbs.db 4 | $RECYCLE.BIN/ 5 | 6 | # macOS 7 | .DS_Store 8 | .fseventsd 9 | .Spotlight-V100 10 | .TemporaryItems 11 | .Trashes 12 | 13 | # Node.js 14 | node_modules/ 15 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "jsxSingleQuote": true, 8 | "jsxBracketSameLine": true, 9 | "trailingComma": "es5" 10 | } 11 | -------------------------------------------------------------------------------- /demo/app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | .container { 3 | height: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: space-between; 8 | padding: 0; 9 | box-sizing: border-box; 10 | } 11 | -------------------------------------------------------------------------------- /demo/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index/index" 4 | ], 5 | "window": { 6 | "backgroundTextStyle": "light", 7 | "navigationBarBackgroundColor": "#fff", 8 | "navigationBarTitleText": "COS 小程序 SDK DEMO", 9 | "navigationBarTextStyle": "black" 10 | }, 11 | "sitemapLocation": "sitemap.json" 12 | } -------------------------------------------------------------------------------- /demo-album/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index/index", 4 | "pages/preview/preview", 5 | "pages/album/album" 6 | ], 7 | "window": { 8 | "navigationBarTitleText": "COS 上传示例", 9 | "windowBackground": "white", 10 | "navigationBarTextStyle": "white", 11 | "navigationBarBackgroundColor": "#373b3e" 12 | }, 13 | "sitemapLocation": "sitemap.json" 14 | } 15 | -------------------------------------------------------------------------------- /scripts/patch-check.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const exec = require('child_process').execSync; 4 | 5 | const patchesDir = path.join(__dirname, '../patches'); 6 | const hasPatches = fs.existsSync(patchesDir); 7 | 8 | if (hasPatches) { 9 | // 执行补丁 10 | console.log('npx patch-package'); 11 | exec('npx patch-package', { stdio: 'inherit' }); 12 | } else { 13 | console.log('无补丁应用'); 14 | } -------------------------------------------------------------------------------- /demo-album/lib/request.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = (options) => { 4 | return new Promise((resolve, reject) => { 5 | options = Object.assign(options, { 6 | success(result) { 7 | if (result.statusCode === 200) { 8 | resolve(result.data); 9 | } else { 10 | reject(result); 11 | } 12 | }, 13 | fail: reject, 14 | }); 15 | wx.request(options); 16 | }); 17 | }; -------------------------------------------------------------------------------- /demo-album/pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 简单上传 4 | 文件 3 天后删除 5 | 6 | 7 | 8 | 9 | 共享相册 10 | 11 | 12 | 13 | ● 上传对象到 COS 并获取下载地址 14 | -------------------------------------------------------------------------------- /demo/demo-ci.js: -------------------------------------------------------------------------------- 1 | const asrDao = require('./ciDemo/asr'); 2 | const auditDao = require('./ciDemo/audit'); 3 | const docPreviewDao = require('./ciDemo/docPreview'); 4 | const picProcessDao = require('./ciDemo/picProcess'); 5 | const mediaProcessDao = require('./ciDemo/mediaProcess'); 6 | const fileProcessDao = require('./ciDemo/fileProcess'); 7 | const metaDao =require('./ciDemo/meta'); 8 | const otherDao = require('./ciDemo/other'); 9 | 10 | module.exports = { 11 | asrDao, 12 | auditDao, 13 | docPreviewDao, 14 | picProcessDao, 15 | mediaProcessDao, 16 | fileProcessDao, 17 | metaDao, 18 | otherDao 19 | }; -------------------------------------------------------------------------------- /patches/fast-xml-parser+4.5.0.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js b/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js 2 | index 70db055..17536b7 100644 3 | --- a/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js 4 | +++ b/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js 5 | @@ -184,7 +184,7 @@ function buildAttributesMap(attrStr, jPath, tagName) { 6 | } 7 | 8 | const parseXml = function(xmlData) { 9 | - xmlData = xmlData.replace(/\r\n?/g, "\n"); //TODO: remove this line 10 | + // xmlData = xmlData.replace(/\r\n?/g, "\n"); //TODO: remove this line 11 | const xmlObj = new xmlNode('!xml'); 12 | let currentNode = xmlObj; 13 | let textData = ""; 14 | -------------------------------------------------------------------------------- /demo-album/project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "", 3 | "setting": { 4 | "urlCheck": true, 5 | "es6": true, 6 | "postcss": true, 7 | "minified": true, 8 | "newFeature": true, 9 | "babelSetting": { 10 | "ignore": [], 11 | "disablePlugins": [], 12 | "outputPath": "" 13 | }, 14 | "coverView": false, 15 | "enhance": false, 16 | "showShadowRootInWxmlPanel": false, 17 | "packNpmRelationList": [], 18 | "ignoreUploadUnusedFiles": true 19 | }, 20 | "compileType": "miniprogram", 21 | "condition": {}, 22 | "editorSetting": { 23 | "tabIndent": "insertSpaces", 24 | "tabSize": 2 25 | }, 26 | "libVersion": "2.28.0", 27 | "packOptions": { 28 | "ignore": [], 29 | "include": [] 30 | }, 31 | "appid": "wx0000000000000000" 32 | } 33 | -------------------------------------------------------------------------------- /demo/app.js: -------------------------------------------------------------------------------- 1 | //app.js 2 | App({ 3 | onLaunch: function () { 4 | //调用API从本地缓存中获取数据 5 | var logs = wx.getStorageSync('logs') || []; 6 | logs.unshift(Date.now()) 7 | wx.setStorageSync('logs', logs) 8 | }, 9 | getUserInfo:function(cb){ 10 | var that = this 11 | if(this.globalData.userInfo){ 12 | typeof cb == "function" && cb(this.globalData.userInfo); 13 | }else{ 14 | //调用登录接口 15 | wx.login({ 16 | success: function () { 17 | wx.getUserInfo({ 18 | success: function (res) { 19 | that.globalData.userInfo = res.userInfo; 20 | typeof cb == "function" && cb(that.globalData.userInfo); 21 | } 22 | }) 23 | } 24 | }) 25 | } 26 | }, 27 | globalData:{ 28 | userInfo:null 29 | } 30 | }); -------------------------------------------------------------------------------- /demo-album/lib/cos.js: -------------------------------------------------------------------------------- 1 | var COS = require('./cos-wx-sdk-v5'); 2 | var config = require('../config'); 3 | var cos = new COS({ 4 | getAuthorization: function (options, callback) { 5 | wx.request({ 6 | method: 'GET', 7 | url: config.stsUrl, // 服务端签名,参考 server 目录下的两个签名例子 8 | dataType: 'json', 9 | success: function (result) { 10 | var data = result.data; 11 | callback({ 12 | TmpSecretId: data.credentials && data.credentials.tmpSecretId, 13 | TmpSecretKey: data.credentials && data.credentials.tmpSecretKey, 14 | XCosSecurityToken: data.credentials && data.credentials.sessionToken, 15 | ExpiredTime: data.expiredTime, 16 | }); 17 | } 18 | }); 19 | }, 20 | }); 21 | 22 | module.exports = cos; -------------------------------------------------------------------------------- /src/event.js: -------------------------------------------------------------------------------- 1 | var initEvent = function (cos) { 2 | var listeners = {}; 3 | var getList = function (action) { 4 | !listeners[action] && (listeners[action] = []); 5 | return listeners[action]; 6 | }; 7 | cos.on = function (action, callback) { 8 | getList(action).push(callback); 9 | }; 10 | cos.off = function (action, callback) { 11 | var list = getList(action); 12 | for (var i = list.length - 1; i >= 0; i--) { 13 | callback === list[i] && list.splice(i, 1); 14 | } 15 | }; 16 | cos.emit = function (action, data) { 17 | var list = getList(action).map(function (cb) { 18 | return cb; 19 | }); 20 | for (var i = 0; i < list.length; i++) { 21 | list[i](data); 22 | } 23 | }; 24 | }; 25 | 26 | var EventProxy = function () { 27 | initEvent(this); 28 | }; 29 | 30 | module.exports.init = initEvent; 31 | module.exports.EventProxy = EventProxy; 32 | -------------------------------------------------------------------------------- /demo-album/pages/preview/preview.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 上传成功 4 | {{url}} 5 | 6 | 复制 URL,可在浏览器中下载您上传的对象 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /demo/pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | /**index.wxss**/ 2 | .btn-content button { 3 | margin: 0 0 10px 0; 4 | border-radius: 0; 5 | } 6 | .title { 7 | display:block; 8 | box-sizing: border-box; 9 | padding: 0 5px; 10 | width: 100%; 11 | height: 30px; 12 | line-height: 30px; 13 | border-top: 1px solid #ccc; 14 | margin:auto; 15 | font-size:14px; 16 | color:#333; 17 | text-align: left; 18 | font-weight: bold; 19 | } 20 | 21 | .list-panel{ 22 | width: 100%; 23 | } 24 | 25 | .sub-title{ 26 | display:block; 27 | box-sizing: border-box; 28 | padding: 0 5px; 29 | width: 100%; 30 | height: 30px; 31 | line-height: 30px; 32 | font-size:12px; 33 | color:#333; 34 | text-align: left; 35 | font-weight: bold; 36 | } 37 | 38 | .list { 39 | margin-top: 10px; 40 | padding-bottom: 10px; 41 | width: 100%; 42 | } 43 | 44 | .button { 45 | float: left; 46 | margin: 0 3px 3px 0; 47 | text-align: left; 48 | font-size: 14px; 49 | height: 28px; 50 | line-height:28px; 51 | padding:0 10px; 52 | } -------------------------------------------------------------------------------- /demo-album/lib/util.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // 一维数组转二维数组 3 | listToMatrix(list, elementsPerSubArray) { 4 | let matrix = [], i, col, row; 5 | for (i = 0, row = -1; i < list.length; i += 1) { 6 | col = i % elementsPerSubArray; 7 | row = Math.floor(i / elementsPerSubArray); 8 | if (!matrix[row]) matrix[row] = [0, 0, 0]; 9 | matrix[row][col] = list[i]; 10 | } 11 | return matrix; 12 | }, 13 | // 选中文件之后,计算一个随机的短文件名 14 | getRandFileName: function (filePath) { 15 | var extIndex = filePath.lastIndexOf('.'); 16 | var extName = extIndex === -1 ? '' : filePath.substr(extIndex); 17 | return parseInt('' + Date.now() + Math.floor(Math.random() * 900 + 100), 10).toString(36) + extName; 18 | }, 19 | // 对更多字符编码的 url encode 格式 20 | camSafeUrlEncode: function (str) { 21 | return encodeURIComponent(str) 22 | .replace(/!/g, '%21') 23 | .replace(/'/g, '%27') 24 | .replace(/\(/g, '%28') 25 | .replace(/\)/g, '%29') 26 | .replace(/\*/g, '%2A'); 27 | }, 28 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-present 腾讯云 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cos-wx-sdk-v5", 3 | "version": "1.8.0", 4 | "description": "小程序 SDK for [腾讯云对象存储服务](https://cloud.tencent.com/product/cos)", 5 | "main": "dist/cos-wx-sdk-v5.min.js", 6 | "scripts": { 7 | "prettier": "prettier --write src demo/demo-sdk.js demo/test.js demo/ciDemo", 8 | "dev": "cross-env NODE_ENV=development node build.js --mode=development", 9 | "build": "cross-env NODE_ENV=production node build.js --mode=production", 10 | "sts.js": "node server/sts.js", 11 | "postinstall": "node scripts/patch-check.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "http://github.com/tencentyun/cos-wx-sdk-v5.git" 16 | }, 17 | "author": "carsonxu", 18 | "license": "ISC", 19 | "dependencies": { 20 | "fast-xml-parser": "4.5.0", 21 | "mime": "^2.4.6" 22 | }, 23 | "devDependencies": { 24 | "@babel/core": "7.17.9", 25 | "@babel/preset-env": "7.16.11", 26 | "babel-loader": "8.2.5", 27 | "body-parser": "^1.18.3", 28 | "cross-env": "^7.0.3", 29 | "express": "^4.17.1", 30 | "patch-package": "^8.0.0", 31 | "prettier": "^3.0.1", 32 | "qcloud-cos-sts": "^3.0.2", 33 | "terser-webpack-plugin": "4.2.3", 34 | "webpack": "4.46.0", 35 | "webpack-cli": "4.10.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/async.js: -------------------------------------------------------------------------------- 1 | var eachLimit = function (arr, limit, iterator, callback) { 2 | callback = callback || function () {}; 3 | if (!arr.length || limit <= 0) { 4 | return callback(); 5 | } 6 | 7 | var completed = 0; 8 | var started = 0; 9 | var running = 0; 10 | 11 | (function replenish() { 12 | if (completed >= arr.length) { 13 | return callback(); 14 | } 15 | 16 | while (running < limit && started < arr.length) { 17 | started += 1; 18 | running += 1; 19 | iterator(arr[started - 1], function (err) { 20 | if (err) { 21 | callback(err); 22 | callback = function () {}; 23 | } else { 24 | completed += 1; 25 | running -= 1; 26 | if (completed >= arr.length) { 27 | callback(); 28 | } else { 29 | replenish(); 30 | } 31 | } 32 | }); 33 | } 34 | })(); 35 | }; 36 | 37 | var retry = function (times, iterator, callback) { 38 | var next = function (index) { 39 | iterator(function (err, data) { 40 | if (err && index < times) { 41 | next(index + 1); 42 | } else { 43 | callback(err, data); 44 | } 45 | }); 46 | }; 47 | if (times < 1) { 48 | callback(); 49 | } else { 50 | next(1); 51 | } 52 | }; 53 | 54 | var async = { 55 | eachLimit: eachLimit, 56 | retry: retry, 57 | }; 58 | 59 | module.exports = async; 60 | -------------------------------------------------------------------------------- /demo/pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 简单上传例子(推荐) 9 | 10 | 11 | 12 | 小程序完整 SDK 例子(功能齐全,文件较大) 13 | 14 | {{title[index]}} {{key}} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 数据万象示例 22 | 23 | {{ciTitle[index]}} {{key}} 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /.github/workflows/auto-changelog.yml: -------------------------------------------------------------------------------- 1 | name: ChangeLog 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [published] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/setup-node@v2-beta 15 | with: 16 | node-version: '12' 17 | - uses: actions/checkout@v2 18 | with: 19 | fetch-depth: 0 20 | 21 | - name: Checkout Tool 22 | uses: actions/checkout@v2 23 | with: 24 | repository: konakonall/auto-changelog 25 | path: 'auto-changelog' 26 | - name: Build Tool 27 | run: | 28 | cd auto-changelog 29 | npm install 30 | npm link 31 | 32 | - name: Generate ChangeLog 33 | env: # Or as an environment variable 34 | TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | run: | 36 | auto-changelog --token $TOKEN 37 | - name: Cat ChangeLog 38 | run: cat CHANGELOG.md 39 | 40 | - name: Commit files 41 | env: 42 | CI_USER: "gouki0123" 43 | CI_EMAIL: "gouki0123@gmail.com" 44 | run: | 45 | git config --local user.email "$CI_EMAIL" 46 | git config --local user.name "$CI_USER" 47 | git add CHANGELOG.md && git commit -m 'Updated CHANGELOG.md' && echo "push=1" >> $GITHUB_ENV || echo "No changes to CHANGELOG.md" 48 | 49 | - name: Push changes 50 | if: env.push == 1 51 | env: 52 | CI_USER: "gouki0123" 53 | CI_TOKEN: ${{ secrets.GITHUB_TOKEN }} 54 | run: | 55 | git push "https://$CI_USER:$CI_TOKEN@github.com/$GITHUB_REPOSITORY.git" HEAD:master 56 | -------------------------------------------------------------------------------- /demo/pages/index/index.js: -------------------------------------------------------------------------------- 1 | //index.js 2 | var cosDemoSdk = require('../../demo-sdk'); 3 | var ciDemoSdk = require('../../demo-ci'); 4 | var postUpload = require('../../demo-post-policy'); 5 | 6 | var option = { 7 | data: { 8 | demoType: 'cos', 9 | listMap: {}, 10 | title: { 11 | toolsDao: '工具函数', 12 | bucketDao: '存储桶操作', 13 | objectDao: '对象操作', 14 | advanceObjectDao: '高级操作', 15 | ciObjectDao: '数据万象示例', 16 | }, 17 | ciListMap: {}, 18 | ciTitle: { 19 | asrDao: '智能语音', 20 | auditDao: '内容审核', 21 | docPreviewDao: '文档预览', 22 | picProcessDao: '图片处理', 23 | mediaProcessDao: '媒体处理', 24 | fileProcessDao: '文件处理', 25 | metaDao: '智能检索', 26 | otherDao: '其他', 27 | }, 28 | }, 29 | switchCosDemo() { 30 | this.setData({ demoType: 'cos' }); 31 | }, 32 | switchCiDemo() { 33 | this.setData({ demoType: 'ci' }); 34 | }, 35 | }; 36 | 37 | for (var key in cosDemoSdk) { 38 | if (cosDemoSdk.hasOwnProperty(key)) { 39 | var sublist = []; 40 | var subDemoSdk = cosDemoSdk[key]; 41 | for (var subkey in subDemoSdk) { 42 | sublist.push(subkey); 43 | option[subkey] = subDemoSdk[subkey]; 44 | } 45 | option.data.listMap[key] = sublist; 46 | } 47 | } 48 | 49 | for (var key in ciDemoSdk) { 50 | if (ciDemoSdk.hasOwnProperty(key)) { 51 | var sublist = []; 52 | var subDemoSdk = ciDemoSdk[key]; 53 | for (var subkey in subDemoSdk) { 54 | sublist.push(subkey); 55 | option[subkey] = subDemoSdk[subkey]; 56 | } 57 | option.data.ciListMap[key] = sublist; 58 | } 59 | } 60 | 61 | option.postUpload = postUpload; 62 | 63 | //获取应用实例 64 | Page(option); 65 | -------------------------------------------------------------------------------- /demo-album/pages/preview/preview.wxss: -------------------------------------------------------------------------------- 1 | .preview { 2 | width: 100%; 3 | height: 100%; 4 | background: #fff; 5 | } 6 | 7 | .preview-success { 8 | display: inline-block; 9 | font-size: 40rpx; 10 | } 11 | 12 | .preview-url { 13 | border: 1px solid #ddd; 14 | padding: 16rpx; 15 | background: #f7f7f7; 16 | word-break:break-all; 17 | font-size: 32rpx; 18 | border-radius: 5rpx; 19 | margin-top: 40rpx; 20 | } 21 | 22 | .preview-tips { 23 | margin-top: 20rpx; 24 | font-size: 30rpx; 25 | color: #888; 26 | } 27 | 28 | .preview-button-wrap { 29 | margin-top: 30rpx; 30 | text-align: left; 31 | } 32 | 33 | .preview-button { 34 | display: inline-block; 35 | position:relative; 36 | text-align: center; 37 | box-sizing:border-box; 38 | font-size:32rpx; 39 | text-decoration:none; 40 | -webkit-tap-highlight-color:transparent; 41 | overflow:hidden; 42 | width:280rpx; 43 | height:85rpx; 44 | background-color:#fff; 45 | color:#2277da; 46 | line-height:85rpx; 47 | border-radius: 5rpx; 48 | } 49 | 50 | .preview-button:first-child { 51 | margin-right: 20rpx; 52 | } 53 | 54 | .preview-outer { 55 | margin-top: 50rpx; 56 | position: relative; 57 | text-align: center; 58 | width: 590rpx; 59 | } 60 | 61 | .preview-inner { 62 | padding-top: 50rpx; 63 | position: relative; 64 | text-align: center; 65 | width: 590rpx; 66 | } 67 | 68 | .preview-image { 69 | display:block; 70 | margin: 0 auto; 71 | width: 590rpx; 72 | height: 590rpx; 73 | } 74 | 75 | .preview-video { 76 | margin-top: 30rpx; 77 | display:block; 78 | margin: 0 auto; 79 | width: 590rpx; 80 | height: 415rpx; 81 | } -------------------------------------------------------------------------------- /demo/project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", 3 | "setting": { 4 | "urlCheck": false, 5 | "es6": false, 6 | "enhance": false, 7 | "postcss": false, 8 | "preloadBackgroundData": false, 9 | "minified": false, 10 | "newFeature": true, 11 | "coverView": true, 12 | "nodeModules": false, 13 | "autoAudits": false, 14 | "showShadowRootInWxmlPanel": true, 15 | "scopeDataCheck": false, 16 | "uglifyFileName": false, 17 | "checkInvalidKey": true, 18 | "checkSiteMap": true, 19 | "uploadWithSourceMap": true, 20 | "compileHotReLoad": false, 21 | "useMultiFrameRuntime": true, 22 | "useApiHook": true, 23 | "useApiHostProcess": true, 24 | "babelSetting": { 25 | "ignore": [], 26 | "disablePlugins": [], 27 | "outputPath": "" 28 | }, 29 | "enableEngineNative": false, 30 | "useIsolateContext": true, 31 | "useCompilerModule": true, 32 | "userConfirmedUseCompilerModuleSwitch": false, 33 | "userConfirmedBundleSwitch": false, 34 | "packNpmManually": false, 35 | "packNpmRelationList": [], 36 | "minifyWXSS": true, 37 | "disableUseStrict": false, 38 | "showES6CompileOption": false, 39 | "useCompilerPlugins": false, 40 | "ignoreUploadUnusedFiles": false, 41 | "useStaticServer": true, 42 | "condition": false 43 | }, 44 | "compileType": "miniprogram", 45 | "libVersion": "2.24.0", 46 | "appid": "wx0000000000000000", 47 | "projectname": "demo", 48 | "simulatorType": "wechat", 49 | "simulatorPluginLibVersion": {}, 50 | "condition": {}, 51 | "packOptions": { 52 | "ignore": [], 53 | "include": [] 54 | }, 55 | "editorSetting": { 56 | "tabIndent": "insertSpaces", 57 | "tabSize": 2 58 | } 59 | } -------------------------------------------------------------------------------- /demo-album/pages/preview/preview.js: -------------------------------------------------------------------------------- 1 | Page({ 2 | data: { 3 | type: 'image', 4 | url: '', 5 | showPreview: false, 6 | }, 7 | onShareAppMessage: function (res) { 8 | var typeMap = { 9 | image: '图片', 10 | video: '视频', 11 | }; 12 | var data = { 13 | title: 'COS 上传示例 - 预览' + (typeMap[this.data.type] || '文件'), 14 | path: this.route + '?type=' + this.data.type + '&url=' + encodeURIComponent(this.data.url), 15 | }; 16 | if (this.data.type === 'image') { 17 | data.imageUrl = this.data.url; 18 | } 19 | return data; 20 | }, 21 | onShow() { 22 | this.setData({showPreview: false}); 23 | this.setData({ 24 | type: this.options.type || 'image', 25 | url: decodeURIComponent(this.options.url) || '', 26 | }); 27 | }, 28 | showPreviewBox() { 29 | this.setData({showPreview: true}); 30 | }, 31 | copyLink() { 32 | wx.setClipboardData({ 33 | data: this.data.url || '', 34 | success: function () { 35 | wx.showToast({title: '复制成功', icon: 'success', duration: 2000}); 36 | }, 37 | fail: function () { 38 | wx.showToast({title: '复制失败', icon: 'error', duration: 2000}); 39 | }, 40 | }); 41 | }, 42 | saveImage() { 43 | wx.downloadFile({ 44 | url: this.data.url, 45 | success: function(res) { 46 | // 只要服务器有响应数据,就会把响应内容写入文件并进入 success 回调,业务需要自行判断是否下载到了想要的内容 47 | if (res.statusCode === 200) { 48 | wx.saveImageToPhotosAlbum({ 49 | filePath: res.tempFilePath, 50 | }); 51 | } 52 | } 53 | }); 54 | }, 55 | }); -------------------------------------------------------------------------------- /demo-album/pages/album/album.wxml: -------------------------------------------------------------------------------- 1 | 2 | 上传图片到 COS 共享给他人查看,长按图片可删除 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 上传图片 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /demo/ciDemo/mediaProcess.js: -------------------------------------------------------------------------------- 1 | const COS = require('../lib/cos-wx-sdk-v5'); 2 | var wxfs = wx.getFileSystemManager(); 3 | const config = require('../config'); 4 | const { cos, requestCallback } = require('../tools'); 5 | 6 | const mediaProcessDao = { 7 | 'describeMediaBuckets 查询已经开通数据万象功能的存储桶': describeMediaBuckets, 8 | 'getMediaInfo 获取媒体文件信息': getMediaInfo, 9 | 'getSnapshot 获取媒体文件某个时间的截图': getSnapshot, 10 | }; 11 | 12 | function describeMediaBuckets() { 13 | var host = 'ci.' + config.Region + '.myqcloud.com'; 14 | var url = 'https://' + host + '/mediabucket'; 15 | cos.request( 16 | { 17 | Bucket: config.Bucket, 18 | Region: config.Region, 19 | Method: 'GET', 20 | Key: 'mediabucket' /** 固定值,必须 */, 21 | Url: url, 22 | Query: { 23 | pageNumber: '1' /** 第几页,非必须 */, 24 | pageSize: '10' /** 每页个数,非必须 */, 25 | // regions: 'ap-chengdu', /** 地域信息,例如'ap-beijing',支持多个值用逗号分隔如'ap-shanghai,ap-beijing',非必须 */ 26 | // bucketNames: 'test-1250000000', /** 存储桶名称,精确搜索,例如'test-1250000000',支持多个值用逗号分隔如'test1-1250000000,test2-1250000000',非必须 */ 27 | // bucketName: 'test', /** 存储桶名称前缀,前缀搜索,例如'test',支持多个值用逗号分隔如'test1,test2',非必须 */ 28 | }, 29 | }, 30 | function (err, data) { 31 | console.log(err || data); 32 | } 33 | ); 34 | } 35 | 36 | function getMediaInfo() { 37 | cos.request( 38 | { 39 | Bucket: config.Bucket, 40 | Region: config.Region, 41 | Method: 'GET', 42 | Key: 'test.mp4', 43 | Query: { 44 | 'ci-process': 'videoinfo' /** 固定值,必须 */, 45 | }, 46 | }, 47 | function (err, data) { 48 | console.log(err || data); 49 | } 50 | ); 51 | } 52 | 53 | function getSnapshot() { 54 | cos.request( 55 | { 56 | Bucket: config.Bucket, 57 | Region: config.Region, 58 | Method: 'GET', 59 | Key: 'test.mp4', 60 | Query: { 61 | 'ci-process': 'snapshot' /** 固定值,必须 */, 62 | time: 1 /** 截图的时间点,单位为秒,必须 */, 63 | // width: 0, /** 截图的宽,非必须 */ 64 | // height: 0, /** 截图的高,非必须 */ 65 | // format: 'jpg', /** 截图的格式,支持 jpg 和 png,默认 jpg,非必须 */ 66 | // rotate: 'auto', /** 图片旋转方式,默认为'auto',非必须 */ 67 | // mode: 'exactframe', /** 截帧方式,默认为'exactframe',非必须 */ 68 | }, 69 | RawBody: true, 70 | DataType: 'arraybuffer', 71 | }, 72 | function (err, data) { 73 | if (err) { 74 | console.log(err); 75 | } else { 76 | const imgBase64 = wx.arrayBufferToBase64(data.Body); 77 | console.log(imgBase64); 78 | } 79 | } 80 | ); 81 | } 82 | 83 | module.exports = mediaProcessDao; 84 | -------------------------------------------------------------------------------- /demo-album/pages/album/album.wxss: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 100%; 3 | height: 100%; 4 | min-height: 242rpx; 5 | } 6 | 7 | .album-container { 8 | margin: 3rpx; 9 | min-height: 242rpx; 10 | marring-top: 83rpx; 11 | } 12 | 13 | .item-group { 14 | display: flex; 15 | } 16 | 17 | .album-item { 18 | flex: 1; 19 | text-align: center; 20 | margin: 3rpx; 21 | width: 242rpx; 22 | height: 242rpx; 23 | line-height: 242rpx; 24 | } 25 | 26 | .album-item.empty { 27 | background: transparent; 28 | } 29 | 30 | .upload-tips { 31 | color: #888; 32 | display: block; 33 | font-size: 30rpx; 34 | height: 80rpx; 35 | line-height: 80rpx; 36 | margin: 0 20rpx; 37 | } 38 | .upload-wrap { 39 | color: #ccc; 40 | background-color: #fff; 41 | border: 1px solid #888; 42 | position: absolute; 43 | left: 6rpx; 44 | top: 86rpx; 45 | width: 240rpx; 46 | height: 239rpx; 47 | text-align: center; 48 | line-height: 240rpx; 49 | overflow: hidden; 50 | } 51 | .upload-add-outer { 52 | display: inline-block; 53 | flex: 1; 54 | text-align: center; 55 | margin: 3rpx; 56 | width: 242rpx; 57 | height: 242rpx; 58 | line-height: 242rpx; 59 | } 60 | .upload-add { 61 | display: block; 62 | position: relative; 63 | text-align: center; 64 | margin: 3rpx; 65 | width: 242rpx; 66 | height: 240rpx; 67 | border: 1rpx solid #ccc; 68 | line-height: 240rpx; 69 | color: #ccc; 70 | overflow: hidden; 71 | box-sizing:border-box; 72 | } 73 | 74 | .upload-add image { 75 | position: absolute; 76 | left: 0; 77 | width: 240rpx; 78 | height: 2.6rem; 79 | top: 1.2rem; 80 | } 81 | 82 | .upload-add text { 83 | position: absolute; 84 | width: 240rpx; 85 | height: 2rem; 86 | left: 0; 87 | top: 1.6rem; 88 | color: #888; 89 | } 90 | 91 | .upload-image { 92 | color: #ccc; 93 | background-color: #fff; 94 | border: 1px solid #888; 95 | position: absolute; 96 | left: 6rpx; 97 | top: 86rpx; 98 | width: 240rpx; 99 | height: 239rpx; 100 | text-align: center; 101 | line-height: 240rpx; 102 | overflow: hidden; 103 | } 104 | 105 | .upload-image image { 106 | position: absolute; 107 | left: 0; 108 | width: 242rpx; 109 | height: 2.6rem; 110 | top: 1.2rem; 111 | } 112 | 113 | .upload-image text { 114 | position: absolute; 115 | width: 242rpx; 116 | height: 2rem; 117 | left: 0; 118 | top: 1.6rem; 119 | color: #888; 120 | } 121 | 122 | .swiper-container { 123 | position: fixed; 124 | left: 0; 125 | top: 0; 126 | width: 100%; 127 | height: 100%; 128 | background: #000; 129 | } 130 | 131 | .swiper-container image { 132 | width: 100%; 133 | height: 100%; 134 | } 135 | 136 | action-sheet-item.warn { 137 | color: #e64340; 138 | } -------------------------------------------------------------------------------- /demo-album/pages/index/index.js: -------------------------------------------------------------------------------- 1 | var util = require('../../lib/util'); 2 | var config = require('../../config'); 3 | var cos = require('../../lib/cos'); 4 | 5 | Page({ 6 | onLoad: function () { 7 | }, 8 | onShareAppMessage: function (res) { 9 | return { 10 | title: 'COS 上传示例', 11 | path: this.route, 12 | } 13 | }, 14 | // 前往相册页 15 | uploadImage() { 16 | wx.chooseImage({ 17 | count: 1, 18 | camera: 'back', 19 | sizeType: ['compressed'], 20 | sourceType: ['album', 'camera'], 21 | success: function (res) { 22 | var filePath = res.tempFilePaths[0]; 23 | if (filePath) { 24 | var Key = util.getRandFileName(filePath); 25 | wx.showLoading({title: '正在上传...'}); 26 | cos.postObject({ 27 | Bucket: config.Bucket, 28 | Region: config.Region, 29 | Key: Key, 30 | FilePath: filePath, 31 | }, function (err, data) { 32 | wx.hideLoading(); 33 | if (data && data.Location) { 34 | wx.navigateTo({url: '../preview/preview?type=image&url=' + encodeURIComponent('https://' + data.Location)}); 35 | } else { 36 | wx.showToast({title: '上传失败', icon: 'error', duration: 2000}); 37 | } 38 | }); 39 | } 40 | } 41 | }) 42 | }, 43 | // 前往相册页 44 | uploadVideo() { 45 | wx.chooseVideo({ 46 | count: 1, 47 | sizeType: ['compressed'], 48 | sourceType: ['album', 'camera'], 49 | maxDuration: 60, 50 | camera: 'back', 51 | success: function (res) { 52 | var filePath = res.tempFilePath; 53 | if (filePath) { 54 | var Key = util.getRandFileName(filePath); 55 | wx.showLoading({title: '正在上传...'}); 56 | cos.postObject({ 57 | Bucket: config.Bucket, 58 | Region: config.Region, 59 | Key: Key, 60 | FilePath: filePath, 61 | }, function (err, data) { 62 | wx.hideLoading(); 63 | if (data && data.Location) { 64 | wx.navigateTo({url: '../preview/preview?type=video&url=' + encodeURIComponent('https://' + data.Location)}); 65 | } else { 66 | wx.showToast({title: '上传失败', icon: 'error', duration: 2000}); 67 | } 68 | }); 69 | } 70 | } 71 | }); 72 | }, 73 | // 前往相册页 74 | gotoAlbum() { 75 | wx.navigateTo({url: '../album/album'}); 76 | }, 77 | }); 78 | -------------------------------------------------------------------------------- /demo/ciDemo/other.js: -------------------------------------------------------------------------------- 1 | const COS = require('../lib/cos-wx-sdk-v5'); 2 | var wxfs = wx.getFileSystemManager(); 3 | const config = require('../config'); 4 | const { cos, requestCallback } = require('../tools'); 5 | 6 | const otherDao = { 7 | '提交病毒检测任务 postVirusDetect': postVirusDetect, 8 | '查询病毒检测任务结果 getVirusDetectResult': getVirusDetectResult, 9 | '查询防盗链 describeRefer': describeRefer, 10 | '设置防盗链 setRefer': setRefer, 11 | }; 12 | 13 | function postVirusDetect() { 14 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com/virus/detect'; 15 | var url = 'https://' + host; 16 | var body = COS.util.json2xml({ 17 | Request: { 18 | Input: { 19 | Object: 'test/1.png', // 文件名,取值为文件在当前存储桶中的完整名称,与Url参数二选一 20 | // Url: 'http://examplebucket-1250000000.cos.ap-shanghai.myqcloud.com/virus.doc', // 病毒文件的链接地址,与Object参数二选一 21 | }, 22 | Conf: { 23 | DetectType: 'Virus', // 检测的病毒类型,当前固定为:Virus 24 | // CallBack: 'http://callback.demo.com', // 任务回调的地址 25 | }, 26 | }, 27 | }); 28 | cos.request( 29 | { 30 | Method: 'POST', 31 | Key: 'virus/detect', 32 | Url: url, 33 | Body: body, 34 | ContentType: 'application/xml', 35 | }, 36 | function (err, data) { 37 | console.log(err || data); 38 | } 39 | ); 40 | } 41 | 42 | function getVirusDetectResult() { 43 | var jobId = 'ss5a8d3065bd9011eda1445254009dadxx'; // 提交病毒检测任务后会返回当前任务的jobId 44 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com/virus/detect/' + jobId; 45 | var url = 'https://' + host; 46 | cos.request( 47 | { 48 | Method: 'GET', 49 | Key: 'virus/detect/' + jobId, 50 | Url: url, 51 | }, 52 | function (err, data) { 53 | console.log(err || data); 54 | } 55 | ); 56 | } 57 | 58 | function describeRefer() { 59 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 60 | const host = config.Bucket + '.pic.' + config.Region + '.myqcloud.com/?hotlink'; 61 | const url = 'https://' + host; 62 | cos.request( 63 | { 64 | Method: 'GET', // 固定值,必须 65 | Url: url, // 请求的url,必须 66 | }, 67 | function (err, data) { 68 | if (err) { 69 | // 处理请求失败 70 | console.log(err); 71 | } else { 72 | // 处理请求成功 73 | console.log(data.Response); 74 | } 75 | } 76 | ); 77 | } 78 | 79 | function setRefer() { 80 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 81 | const host = config.Bucket + '.pic.' + config.Region + '.myqcloud.com/?hotlink'; 82 | const url = 'https://' + host; 83 | const body = COS.util.json2xml({ 84 | Hotlink: { 85 | Url: 'https://www.example.com', // 必须,域名地址 86 | Type: 'white', // 必须,防盗链类型,white 为白名单,black 为黑名单,off 为关闭。 87 | }, 88 | }); 89 | cos.request( 90 | { 91 | Method: 'PUT', 92 | Url: url, 93 | Body: body, 94 | }, 95 | function (err, data) { 96 | console.log(err || data); 97 | } 98 | ); 99 | } 100 | 101 | module.exports = otherDao; 102 | -------------------------------------------------------------------------------- /demo/demo-post-policy.js: -------------------------------------------------------------------------------- 1 | var config = require('./config'); 2 | 3 | var uploadFile = function () { 4 | // 对更多字符编码的 url encode 格式 5 | var camSafeUrlEncode = function (str) { 6 | return encodeURIComponent(str) 7 | .replace(/!/g, '%21') 8 | .replace(/'/g, '%27') 9 | .replace(/\(/g, '%28') 10 | .replace(/\)/g, '%29') 11 | .replace(/\*/g, '%2A'); 12 | }; 13 | 14 | // 获取临时密钥 15 | var getCredentials = function (options, callback) { 16 | wx.request({ 17 | method: 'GET', 18 | url: 'http://127.0.0.1:3000/post-policy?ext=' + options.ext, // 服务端签名,参考 server 目录下的两个签名例子 19 | dataType: 'json', 20 | success: function (result) { 21 | var data = result.data; 22 | if (data) { 23 | callback(data); 24 | } else { 25 | wx.showModal({ title: '临时密钥获取失败', content: JSON.stringify(data), showCancel: false }); 26 | } 27 | }, 28 | error: function (err) { 29 | wx.showModal({ title: '临时密钥获取失败', content: JSON.stringify(err), showCancel: false }); 30 | }, 31 | }); 32 | }; 33 | 34 | // 上传文件 35 | var uploadFile = function (filePath) { 36 | var extIndex = filePath.lastIndexOf('.'); 37 | var fileExt = extIndex >= -1 ? filePath.substr(extIndex + 1) : ''; 38 | getCredentials({ ext: fileExt }, function (credentials) { 39 | // 请求用到的参数 40 | // var prefix = 'https://cos.' + config.Region + '.myqcloud.com/' + config.Bucket + '/'; // 这个是后缀式,签名也要指定 Pathname: '/' + config.Bucket + '/' 41 | var prefix = 'https://' + credentials.bucket + '.cos.' + credentials.region + '.myqcloud.com/'; 42 | var key = credentials.key; // 让服务端来决定文件名更安全 43 | var formData = { 44 | key: key, 45 | success_action_status: 200, 46 | 'Content-Type': '', 47 | 'q-sign-algorithm': credentials.qSignAlgorithm, 48 | 'q-ak': credentials.qAk, 49 | 'q-key-time': credentials.qKeyTime, 50 | 'q-signature': credentials.qSignature, 51 | policy: credentials.policy, 52 | }; 53 | if (credentials.securityToken) formData['x-cos-security-token'] = credentials.securityToken; 54 | var requestTask = wx.uploadFile({ 55 | url: prefix, 56 | name: 'file', 57 | filePath: filePath, 58 | formData: formData, 59 | success: function (res) { 60 | var url = prefix + camSafeUrlEncode(key).replace(/%2F/g, '/'); 61 | if (res.statusCode === 200) { 62 | wx.showModal({ title: '上传成功', content: url, showCancel: false }); 63 | } else { 64 | wx.showModal({ title: '上传失败', content: JSON.stringify(res), showCancel: false }); 65 | } 66 | console.log(res.header['x-cos-request-id']); 67 | console.log(res.statusCode); 68 | console.log(url); 69 | }, 70 | fail: function (res) { 71 | wx.showModal({ title: '上传失败', content: JSON.stringify(res), showCancel: false }); 72 | }, 73 | }); 74 | requestTask.onProgressUpdate(function (res) { 75 | console.log('正在进度:', res); 76 | }); 77 | }); 78 | }; 79 | 80 | // 选择文件 81 | wx.chooseMedia({ 82 | count: 1, // 默认9 83 | mediaType: ['image', 'video'], 84 | sizeType: ['original'], 85 | sourceType: ['album', 'camera'], 86 | success: function (res) { 87 | uploadFile(res.tempFiles[0].tempFilePath); 88 | }, 89 | }); 90 | }; 91 | 92 | module.exports = uploadFile; 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 快速入门 2 | 3 | 微信小程序 SDK for [腾讯云对象存储服务](https://cloud.tencent.com/product/cos) 4 | 5 | ### 一、前期准备 6 | 7 | 1. 到 (COS对象存储控制台)[https://console.cloud.tencent.com/cos4] 创建存储桶,得到 Bucket(存储桶名称) 和 Region(地域名称) 8 | 2. 到 (控制台密钥管理)[https://console.cloud.tencent.com/capi] 获取您的项目 SecretId 和 SecretKey 9 | 10 | ### 二、计算签名 11 | 12 | 由于签名计算放在前端会暴露 SecretId 和 SecretKey,我们把签名计算过程放在后端实现,前端通过 ajax 向后端获取签名结果,正式部署时请再后端加一层自己网站本身的权限检验。 13 | 14 | 这里提供 [NodeJS 的签名示例](https://github.com/tencentyun/cos-js-sdk-v5/blob/master/server/),其他语言,请参照对应的 [XML SDK](https://cloud.tencent.com/document/product/436/6474) 15 | 16 | ### 三、上传例子 17 | 18 | 1. 把 demo/lib/cos-wx-sdk-v5.js 复制到自己小程序项目代码里,在需要上传文件的地方贴以下代码 19 | 20 | ```javascript 21 | // 存储桶名称,由bucketname-appid 组成,appid必须填入,可以在COS控制台查看存储桶名称。 https://console.cloud.tencent.com/cos5/bucket 22 | const Bucket = 'test-1250000000'; 23 | // 存储桶Region可以在COS控制台指定存储桶的概览页查看 https://console.cloud.tencent.com/cos5/bucket/ 24 | // 关于地域的详情见 https://cloud.tencent.com/document/product/436/6224 25 | const Region = 'ap-guangzhou'; 26 | 27 | // 初始化实例 28 | const cos = new COS({ 29 | SimpleUploadMethod: 'putObject', // 强烈建议,高级上传、批量上传内部对小文件做简单上传时使用putObject,sdk版本至少需要v1.3.0 30 | getAuthorization: function (options, callback) { 31 | // 初始化时不会调用,只有调用 cos 方法(例如 cos.putObject)时才会进入 32 | // 异步获取临时密钥 33 | // 服务端 JS 示例:https://github.com/tencentyun/cos-js-sdk-v5/blob/master/server/ 34 | // 服务端其他语言参考 COS STS SDK :https://github.com/tencentyun/qcloud-cos-sts-sdk 35 | // STS 详细文档指引看:https://cloud.tencent.com/document/product/436/14048 36 | wx.request({ 37 | url: 'https://example.com/server/sts', // url 替换成您自己的后端服务 38 | data: { 39 | bucket: options.Bucket, 40 | region: options.Region, 41 | }, 42 | dataType: 'json', 43 | success: function (result) { 44 | const data = result.data; 45 | const credentials = data && data.credentials; 46 | if (!data || !credentials) return console.error('credentials invalid'); 47 | // 检查credentials格式 48 | console.log(credentials); 49 | callback({ 50 | TmpSecretId: credentials.tmpSecretId, 51 | TmpSecretKey: credentials.tmpSecretKey, 52 | // v1.2.0之前版本的 SDK 使用 XCosSecurityToken 而不是 SecurityToken 53 | SecurityToken: credentials.sessionToken, 54 | // 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误 55 | StartTime: data.startTime, // 时间戳,单位秒,如:1580000000 56 | ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000900 57 | }); 58 | }, 59 | }); 60 | }, 61 | }); 62 | 63 | // 选择文件 64 | wx.chooseMedia({ 65 | count: 1, // 默认9 66 | mediaType: ['image', 'video'], 67 | sizeType: ['original'], 68 | sourceType: ['album', 'camera'], 69 | success: function (res) { 70 | const filePath = res.tempFiles[0].tempFilePath; 71 | const filename = filePath.substr(filePath.lastIndexOf('/') + 1); 72 | cos.postObject( 73 | { 74 | Bucket: Bucket, 75 | Region: Region, 76 | Key: filename, 77 | FilePath: filePath, 78 | onProgress: function (info) { 79 | console.log(JSON.stringify(info)); 80 | }, 81 | }, 82 | function (err, data) { 83 | console.log(err || data); 84 | } 85 | ); 86 | }, 87 | }); 88 | ``` 89 | 90 | ## 说明文档 91 | 92 | [使用例子](demo/demo-sdk.js) 93 | 94 | [快速入门](https://cloud.tencent.com/document/product/436/31953) 95 | 96 | [接口文档](https://cloud.tencent.com/document/product/436/31953) 97 | -------------------------------------------------------------------------------- /src/session.js: -------------------------------------------------------------------------------- 1 | var util = require('./util'); 2 | 3 | // 按照文件特征值,缓存 UploadId 4 | var cacheKey = 'cos_sdk_upload_cache'; 5 | var expires = 30 * 24 * 3600; 6 | var cache; 7 | var timer; 8 | 9 | var getCache = function () { 10 | try { 11 | var val = JSON.parse(wx.getStorageSync(cacheKey)); 12 | } catch (e) {} 13 | if (!val) val = []; 14 | return val; 15 | }; 16 | var setCache = function () { 17 | try { 18 | if (cache.length) wx.setStorageSync(cacheKey, JSON.stringify(cache)); 19 | else wx.removeStorageSync(cacheKey); 20 | } catch (e) {} 21 | }; 22 | 23 | var init = function () { 24 | if (cache) return; 25 | cache = getCache(); 26 | // 清理太老旧的数据 27 | var changed = false; 28 | var now = Math.round(Date.now() / 1000); 29 | for (var i = cache.length - 1; i >= 0; i--) { 30 | var mtime = cache[i][2]; 31 | if (!mtime || mtime + expires < now) { 32 | cache.splice(i, 1); 33 | changed = true; 34 | } 35 | } 36 | changed && setCache(); 37 | }; 38 | 39 | // 把缓存存到本地 40 | var save = function () { 41 | if (timer) return; 42 | timer = setTimeout(function () { 43 | setCache(); 44 | timer = null; 45 | }, 400); 46 | }; 47 | 48 | var mod = { 49 | using: {}, 50 | // 标记 UploadId 正在使用 51 | setUsing: function (uuid) { 52 | mod.using[uuid] = true; 53 | }, 54 | // 标记 UploadId 已经没在使用 55 | removeUsing: function (uuid) { 56 | delete mod.using[uuid]; 57 | }, 58 | // 用上传参数生成哈希值 59 | getFileId: function (FileStat, ChunkSize, Bucket, Key) { 60 | if (FileStat.FilePath && FileStat.size && FileStat.lastModifiedTime && ChunkSize) { 61 | return ( 62 | util.md5([FileStat.FilePath].join('::')) + 63 | '-' + 64 | util.md5( 65 | [ 66 | FileStat.size, 67 | FileStat.mode, 68 | FileStat.lastAccessedTime, 69 | FileStat.lastModifiedTime, 70 | ChunkSize, 71 | Bucket, 72 | Key, 73 | ].join('::') 74 | ) 75 | ); 76 | } else { 77 | return null; 78 | } 79 | }, 80 | // 用上传参数生成哈希值 81 | getCopyFileId: function (copySource, sourceHeaders, ChunkSize, Bucket, Key) { 82 | var size = sourceHeaders['content-length']; 83 | var etag = sourceHeaders.etag || ''; 84 | var lastModified = sourceHeaders['last-modified']; 85 | if (copySource && ChunkSize) { 86 | return util.md5([copySource, size, etag, lastModified, ChunkSize, Bucket, Key].join('::')); 87 | } else { 88 | return null; 89 | } 90 | }, 91 | // 获取文件对应的 UploadId 列表 92 | getUploadIdList: function (uuid) { 93 | if (!uuid) return null; 94 | init(); 95 | var list = []; 96 | for (var i = 0; i < cache.length; i++) { 97 | if (cache[i][0] === uuid) list.push(cache[i][1]); 98 | } 99 | return list.length ? list : null; 100 | }, 101 | // 缓存 UploadId 102 | saveUploadId: function (uuid, UploadId, limit) { 103 | init(); 104 | if (!uuid) return; 105 | // 清理没用的 UploadId 106 | var part1 = uuid.substr(0, uuid.indexOf('-') + 1); 107 | for (var i = cache.length - 1; i >= 0; i--) { 108 | var item = cache[i]; 109 | if (item[0] === uuid && item[1] === UploadId) { 110 | cache.splice(i, 1); 111 | } else if (uuid !== item[0] && item[0].indexOf(part1) === 0) { 112 | // 文件路径相同,但其他信息不同,说明文件改变了或上传参数(存储桶、路径、分片大小)变了,直接清理掉 113 | cache.splice(i, 1); 114 | } 115 | } 116 | cache.unshift([uuid, UploadId, Math.round(Date.now() / 1000)]); 117 | if (cache.length > limit) cache.splice(limit); 118 | save(); 119 | }, 120 | // UploadId 已用完,移除掉 121 | removeUploadId: function (UploadId) { 122 | init(); 123 | delete mod.using[UploadId]; 124 | for (var i = cache.length - 1; i >= 0; i--) { 125 | if (cache[i][1] === UploadId) cache.splice(i, 1); 126 | } 127 | save(); 128 | }, 129 | }; 130 | 131 | module.exports = mod; 132 | -------------------------------------------------------------------------------- /src/cos.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('./util'); 4 | var event = require('./event'); 5 | var task = require('./task'); 6 | var base = require('./base'); 7 | var advance = require('./advance'); 8 | var pkg = require('../package.json'); 9 | 10 | var defaultOptions = { 11 | SecretId: '', 12 | SecretKey: '', 13 | SecurityToken: '', // 使用临时密钥需要注意自行刷新 Token 14 | StartTime: 0, // 临时密钥返回起始时间 15 | ExpiredTime: 0, // 临时密钥过期时间 16 | ChunkRetryTimes: 2, 17 | FileParallelLimit: 3, 18 | ChunkParallelLimit: 3, 19 | ChunkSize: 1024 * 1024, 20 | SliceSize: 1024 * 1024, 21 | CopyChunkParallelLimit: 20, 22 | CopyChunkSize: 1024 * 1024 * 10, 23 | CopySliceSize: 1024 * 1024 * 10, 24 | MaxPartNumber: 10000, 25 | ProgressInterval: 1000, 26 | UploadQueueSize: 10000, 27 | Domain: '', 28 | ServiceDomain: '', 29 | Protocol: '', 30 | CompatibilityMode: false, 31 | ForcePathStyle: false, 32 | Timeout: 0, // 单位毫秒,0 代表不设置超时时间 33 | CorrectClockSkew: true, 34 | SystemClockOffset: 0, // 单位毫秒,ms 35 | UploadCheckContentMd5: false, 36 | UploadAddMetaMd5: false, 37 | UploadIdCacheLimit: 50, 38 | UseAccelerate: false, 39 | ForceSignHost: true, // 默认将host加入签名计算,关闭后可能导致越权风险,建议保持为true 40 | HttpDNSServiceId: '', // HttpDNS 服务商 Id,填写后代表开启 HttpDNS 服务。HttpDNS 用法详见https://developers.weixin.qq.com/miniprogram/dev/framework/ability/HTTPDNS.html 41 | SimpleUploadMethod: 'postObject', // 高级上传内部判断需要走简单上传时,指定的上传方法,可选postObject或putObject 42 | AutoSwitchHost: false, 43 | CopySourceParser: null, // 自定义拷贝源解析器 44 | ObjectKeySimplifyCheck: true, // 开启合并校验 getObject Key 45 | /** 上报相关配置 **/ 46 | DeepTracker: false, // 上报时是否对每个分块上传做单独上报 47 | TrackerDelay: 5000, // 周期性上报,单位毫秒。0代表实时上报 48 | CustomId: '', // 自定义上报id 49 | BeaconReporter: null, // 灯塔上报组件,如有需要请自行传入,传入即代表开启上报 50 | ClsReporter: null, // cls 上报组件,如有需要请自行传入,传入即代表开启上报 51 | }; 52 | 53 | // 对外暴露的类 54 | var COS = function (options) { 55 | this.options = util.extend(util.clone(defaultOptions), options || {}); 56 | this.options.FileParallelLimit = Math.max(1, this.options.FileParallelLimit); 57 | this.options.ChunkParallelLimit = Math.max(1, this.options.ChunkParallelLimit); 58 | this.options.ChunkRetryTimes = Math.max(0, this.options.ChunkRetryTimes); 59 | this.options.ChunkSize = Math.max(1024 * 1024, this.options.ChunkSize); 60 | this.options.CopyChunkParallelLimit = Math.max(1, this.options.CopyChunkParallelLimit); 61 | this.options.CopyChunkSize = Math.max(1024 * 1024, this.options.CopyChunkSize); 62 | this.options.CopySliceSize = Math.max(0, this.options.CopySliceSize); 63 | this.options.MaxPartNumber = Math.max(1024, Math.min(10000, this.options.MaxPartNumber)); 64 | this.options.Timeout = Math.max(0, this.options.Timeout); 65 | this.options.EnableReporter = this.options.BeaconReporter || this.options.ClsReporter; 66 | if (this.options.AppId) { 67 | console.warn( 68 | 'warning: AppId has been deprecated, Please put it at the end of parameter Bucket(E.g: "test-1250000000").' 69 | ); 70 | } 71 | if (this.options.SecretId && this.options.SecretId.indexOf(' ') > -1) { 72 | console.error('error: SecretId格式错误,请检查'); 73 | console.error('error: SecretId format is incorrect. Please check'); 74 | } 75 | if (this.options.SecretKey && this.options.SecretKey.indexOf(' ') > -1) { 76 | console.error('error: SecretKey格式错误,请检查'); 77 | console.error('error: SecretKey format is incorrect. Please check'); 78 | } 79 | if (this.options.ForcePathStyle) { 80 | console.warn( 81 | 'cos-wx-sdk-v5不再支持使用path-style,仅支持使用virtual-hosted-style,参考文档:https://cloud.tencent.com/document/product/436/96243' 82 | ); 83 | throw new Error('ForcePathStyle is not supported'); 84 | } 85 | event.init(this); 86 | task.init(this); 87 | }; 88 | 89 | base.init(COS, task); 90 | advance.init(COS, task); 91 | 92 | COS.util = { 93 | md5: util.md5, 94 | xml2json: util.xml2json, 95 | json2xml: util.json2xml, 96 | encodeBase64: util.encodeBase64, 97 | }; 98 | COS.getAuthorization = util.getAuth; 99 | COS.version = pkg.version; 100 | 101 | module.exports = COS; 102 | -------------------------------------------------------------------------------- /lib/request.js: -------------------------------------------------------------------------------- 1 | function camSafeUrlEncode(str) { 2 | return encodeURIComponent(str) 3 | .replace(/!/g, '%21') 4 | .replace(/'/g, '%27') 5 | .replace(/\(/g, '%28') 6 | .replace(/\)/g, '%29') 7 | .replace(/\*/g, '%2A'); 8 | } 9 | 10 | function getObjectKeys(obj, forKey) { 11 | var list = []; 12 | for (var key in obj) { 13 | if (obj.hasOwnProperty(key)) { 14 | list.push(forKey ? camSafeUrlEncode(key).toLowerCase() : key); 15 | } 16 | } 17 | return list.sort(function (a, b) { 18 | a = a.toLowerCase(); 19 | b = b.toLowerCase(); 20 | return a === b ? 0 : (a > b ? 1 : -1); 21 | }); 22 | }; 23 | 24 | var obj2str = function (obj, lowerCaseKey) { 25 | var i, key, val; 26 | var list = []; 27 | var keyList = getObjectKeys(obj); 28 | for (i = 0; i < keyList.length; i++) { 29 | key = keyList[i]; 30 | val = (obj[key] === undefined || obj[key] === null) ? '' : ('' + obj[key]); 31 | key = lowerCaseKey ? camSafeUrlEncode(key).toLowerCase() : camSafeUrlEncode(key); 32 | val = camSafeUrlEncode(val) || ''; 33 | list.push(key + '=' + val) 34 | } 35 | return list.join('&'); 36 | }; 37 | 38 | var request = function (params, callback) { 39 | var filePath = params.filePath; 40 | var headers = params.headers || {}; 41 | var url = params.url || params.Url; 42 | var method = params.method; 43 | var onProgress = params.onProgress; 44 | var httpDNSServiceId = params.httpDNSServiceId; 45 | var requestTask; 46 | 47 | var cb = function (err, response) { 48 | var H = response.header 49 | var headers = {}; 50 | if (H) for (var key in H) { 51 | if (H.hasOwnProperty(key)) headers[key.toLowerCase()] = H[key]; 52 | } 53 | callback(err, { statusCode: response.statusCode, headers: headers }, response.data); 54 | }; 55 | 56 | if (filePath) { 57 | var fileKey; 58 | var m = url.match(/^(https?:\/\/[^/]+\/)([^/]*\/?)(.*)$/); 59 | if (params.pathStyle) { 60 | fileKey = decodeURIComponent(m[3] || ''); 61 | url = m[1] + m[2]; 62 | } else { 63 | fileKey = decodeURIComponent(m[2] + m[3] || ''); 64 | url = m[1]; 65 | } 66 | 67 | // 整理 postObject 参数 68 | var formData = { 69 | 'key': fileKey, 70 | 'success_action_status': 200, 71 | 'Signature': headers.Authorization, 72 | }; 73 | var headerKeys = [ 74 | 'Cache-Control', 75 | 'Content-Type', 76 | 'Content-Disposition', 77 | 'Content-Encoding', 78 | 'Expires', 79 | 'x-cos-storage-class', 80 | 'x-cos-security-token', 81 | 'x-ci-security-token', 82 | ]; 83 | for (var i in params.headers) { 84 | if (params.headers.hasOwnProperty(i) && (i.indexOf('x-cos-meta-') > -1 || headerKeys.indexOf(i) > -1)) { 85 | formData[i] = params.headers[i]; 86 | } 87 | } 88 | headers['x-cos-acl'] && (formData.acl = headers['x-cos-acl']); 89 | !formData['Content-Type'] && (formData['Content-Type'] = ''); 90 | 91 | requestTask = wx.uploadFile({ 92 | url: url, 93 | method: method, 94 | name: 'file', 95 | header: headers, 96 | filePath: filePath, 97 | formData: formData, 98 | timeout: params.timeout, 99 | success: function (response) { 100 | cb(null, response); 101 | }, 102 | fail: function (response) { 103 | cb(response.errMsg, response); 104 | } 105 | }); 106 | requestTask.onProgressUpdate(function (res) { 107 | onProgress && onProgress({ 108 | loaded: res.totalBytesSent, 109 | total: res.totalBytesExpectedToSend, 110 | progress: res.progress / 100 111 | }); 112 | }); 113 | } else { 114 | var qsStr = params.qs && obj2str(params.qs) || ''; 115 | if (qsStr) { 116 | url += (url.indexOf('?') > -1 ? '&' : '?') + qsStr; 117 | } 118 | headers['Content-Length'] && (delete headers['Content-Length']); 119 | var requestParams = { 120 | url: url, 121 | method: method, 122 | header: headers, 123 | dataType: 'text', 124 | data: params.body, 125 | responseType: params.dataType || 'text', 126 | timeout: params.timeout, 127 | redirect: 'manual', 128 | success: function (response) { 129 | cb(null, response); 130 | }, 131 | fail: function (response) { 132 | cb(response.errMsg, response); 133 | } 134 | }; 135 | if (httpDNSServiceId) { 136 | Object.assign(requestParams, { 137 | enableHttpDNS: true, 138 | httpDNSServiceId: httpDNSServiceId, 139 | }); 140 | } 141 | requestTask = wx.request(requestParams); 142 | } 143 | 144 | return requestTask; 145 | }; 146 | 147 | module.exports = request; 148 | -------------------------------------------------------------------------------- /demo/tools.js: -------------------------------------------------------------------------------- 1 | var COS = require('./lib/cos-wx-sdk-v5'); 2 | var config = require('./config'); 3 | const Beacon = require('./lib/beacon_mp.min'); 4 | const ClsClient = require('./lib/cls.min'); 5 | const clsClient = new ClsClient({ 6 | topicId: 'xxxxxx-xxxx-xxxx-xxxx-xxxxxx', // 日志主题 id 7 | region: 'ap-guangzhou', // 日志主题所在地域,比如 ap-guangzhou,需在小程序平台设置域名白名单:https://ap-guangzhou.cls.tencentcs.com 8 | maxRetainDuration: 30, // 默认 30s 9 | maxRetainSize: 20, // 默认20条 10 | }); 11 | 12 | // 签名回调 13 | var getAuthorization = function (options, callback) { 14 | // 格式一、(推荐)后端通过获取临时密钥给到前端,前端计算签名 15 | // 服务端 JS 和 PHP 例子:https://github.com/tencentyun/cos-js-sdk-v5/blob/master/server/ 16 | // 服务端其他语言参考 COS STS SDK :https://github.com/tencentyun/qcloud-cos-sts-sdk 17 | wx.request({ 18 | method: 'GET', 19 | url: config.stsUrl, // 服务端签名,参考 server 目录下的两个签名例子 20 | dataType: 'json', 21 | success: function (result) { 22 | var data = result.data; 23 | var credentials = data && data.credentials; 24 | if (!data || !credentials) return console.error('credentials invalid'); 25 | callback({ 26 | TmpSecretId: credentials.tmpSecretId, 27 | TmpSecretKey: credentials.tmpSecretKey, 28 | SecurityToken: credentials.sessionToken, 29 | StartTime: data.startTime, // 时间戳,单位秒,如:1580000000,建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误 30 | ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000900 31 | }); 32 | }, 33 | }); 34 | 35 | // // 格式二、(推荐)【细粒度控制权限】后端通过获取临时密钥给到前端,前端只有相同请求才重复使用临时密钥,后端可以通过 Scope 细粒度控制权限 36 | // // 服务端例子:https://github.com/tencentyun/qcloud-cos-sts-sdk/edit/master/scope.md 37 | // wx.request({ 38 | // method: 'POST', 39 | // url: 'http://127.0.0.1:3000/sts-scope', 40 | // data: options.Scope, 41 | // dataType: 'json', 42 | // success: function(result) { 43 | // var data = result.data; 44 | // var credentials = data && data.credentials; 45 | // if (!data || !credentials) return console.error('credentials invalid'); 46 | // callback({ 47 | // TmpSecretId: credentials.tmpSecretId, 48 | // TmpSecretKey: credentials.tmpSecretKey, 49 | // XCosSecurityToken: credentials.sessionToken, 50 | // StartTime: data.startTime, // 时间戳,单位秒,如:1580000000,建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误 51 | // ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000900 52 | // ScopeLimit: true, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用 53 | // }); 54 | // } 55 | // }); 56 | 57 | // // 格式三、(不推荐,分片上传权限不好控制)前端每次请求前都需要通过 getAuthorization 获取签名,后端使用固定密钥或临时密钥计算签名返回给前端 58 | // // 服务端获取签名,请参考对应语言的 COS SDK:https://cloud.tencent.com/document/product/436/6474 59 | // // 注意:这种有安全风险,后端需要通过 method、pathname 严格控制好权限,比如不允许 put / 等 60 | // wx.request({ 61 | // method: 'POST', 62 | // url: 'https://example.com/sts-auth.php, // 服务端签名,参考 server 目录下的两个签名例子 63 | // data: { 64 | // method: options.Method, 65 | // pathname: options.Pathname, 66 | // query: options.Query, 67 | // headers: options.Headers, 68 | // }, 69 | // dataType: 'json', 70 | // success: function(result) { 71 | // var data = result.data; 72 | // if (!data || !data.authorization) return console.error('authorization invalid'); 73 | // callback({ 74 | // Authorization: data.authorization, 75 | // // XCosSecurityToken: data.sessionToken, // 如果使用临时密钥,需要传 sessionToken 76 | // }); 77 | // } 78 | // }); 79 | 80 | // // 格式四、(不推荐,适用于前端调试,避免泄露密钥)前端使用固定密钥计算签名 81 | // var authorization = COS.getAuthorization({ 82 | // SecretId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 83 | // SecretKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 84 | // Method: options.Method, 85 | // Pathname: options.Pathname, 86 | // Query: options.Query, 87 | // Headers: options.Headers, 88 | // Expires: 60, 89 | // }); 90 | // callback({ 91 | // Authorization: authorization, 92 | // // XCosSecurityToken: credentials.sessionToken, // 如果使用临时密钥,需要传 XCosSecurityToken 93 | // }); 94 | }; 95 | 96 | var cos = new COS({ 97 | getAuthorization: getAuthorization, 98 | // 是否使用全球加速域名。开启该配置后仅以下接口支持操作:putObject、getObject、headObject、optionsObject、multipartInit、multipartListPart、multipartUpload、multipartAbort、multipartComplete、multipartList、sliceUploadFile、uploadFiles 99 | // UseAccelerate: true, 100 | // BeaconReporter: Beacon, // 开启灯塔上报 101 | // ClsReporter: clsClient, // 开启 cls 上报 102 | }); 103 | 104 | // 回调统一处理函数 105 | var requestCallback = function (err, data) { 106 | console.log(err || data); 107 | if (err && err.error) { 108 | wx.showModal({ 109 | title: '返回错误', 110 | content: '请求失败:' + (err.error.Message || err.error) + ';状态码:' + err.statusCode, 111 | showCancel: false, 112 | }); 113 | } else if (err) { 114 | wx.showModal({ 115 | title: '请求出错', 116 | content: '请求出错:' + err + ';状态码:' + err.statusCode, 117 | showCancel: false, 118 | }); 119 | } else { 120 | wx.showToast({ 121 | title: '请求成功', 122 | icon: 'success', 123 | duration: 3000, 124 | }); 125 | } 126 | }; 127 | 128 | module.exports = { 129 | cos, 130 | requestCallback, 131 | }; -------------------------------------------------------------------------------- /lib/base64.js: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: base64.js,v 2.15 2014/04/05 12:58:57 dankogai Exp dankogai $ 3 | * 4 | * Licensed under the BSD 3-Clause License. 5 | * http://opensource.org/licenses/BSD-3-Clause 6 | * 7 | * References: 8 | * http://en.wikipedia.org/wiki/Base64 9 | */ 10 | 11 | var Base64 = (function(global) { 12 | global = global || {}; 13 | 'use strict'; 14 | // existing version for noConflict() 15 | var _Base64 = global.Base64; 16 | var version = "2.1.9"; 17 | // if node.js, we use Buffer 18 | var buffer; 19 | // constants 20 | var b64chars 21 | = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 22 | var b64tab = function(bin) { 23 | var t = {}; 24 | for (var i = 0, l = bin.length; i < l; i++) t[bin.charAt(i)] = i; 25 | return t; 26 | }(b64chars); 27 | var fromCharCode = String.fromCharCode; 28 | // encoder stuff 29 | var cb_utob = function(c) { 30 | if (c.length < 2) { 31 | var cc = c.charCodeAt(0); 32 | return cc < 0x80 ? c 33 | : cc < 0x800 ? (fromCharCode(0xc0 | (cc >>> 6)) 34 | + fromCharCode(0x80 | (cc & 0x3f))) 35 | : (fromCharCode(0xe0 | ((cc >>> 12) & 0x0f)) 36 | + fromCharCode(0x80 | ((cc >>> 6) & 0x3f)) 37 | + fromCharCode(0x80 | ( cc & 0x3f))); 38 | } else { 39 | var cc = 0x10000 40 | + (c.charCodeAt(0) - 0xD800) * 0x400 41 | + (c.charCodeAt(1) - 0xDC00); 42 | return (fromCharCode(0xf0 | ((cc >>> 18) & 0x07)) 43 | + fromCharCode(0x80 | ((cc >>> 12) & 0x3f)) 44 | + fromCharCode(0x80 | ((cc >>> 6) & 0x3f)) 45 | + fromCharCode(0x80 | ( cc & 0x3f))); 46 | } 47 | }; 48 | var re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g; 49 | var utob = function(u) { 50 | return u.replace(re_utob, cb_utob); 51 | }; 52 | var cb_encode = function(ccc) { 53 | var padlen = [0, 2, 1][ccc.length % 3], 54 | ord = ccc.charCodeAt(0) << 16 55 | | ((ccc.length > 1 ? ccc.charCodeAt(1) : 0) << 8) 56 | | ((ccc.length > 2 ? ccc.charCodeAt(2) : 0)), 57 | chars = [ 58 | b64chars.charAt( ord >>> 18), 59 | b64chars.charAt((ord >>> 12) & 63), 60 | padlen >= 2 ? '=' : b64chars.charAt((ord >>> 6) & 63), 61 | padlen >= 1 ? '=' : b64chars.charAt(ord & 63) 62 | ]; 63 | return chars.join(''); 64 | }; 65 | var btoa = global.btoa ? function(b) { 66 | return global.btoa(b); 67 | } : function(b) { 68 | return b.replace(/[\s\S]{1,3}/g, cb_encode); 69 | }; 70 | var _encode = buffer ? function (u) { 71 | return (u.constructor === buffer.constructor ? u : new buffer(u)) 72 | .toString('base64') 73 | } 74 | : function (u) { return btoa(utob(u)) } 75 | ; 76 | var encode = function(u, urisafe) { 77 | return !urisafe 78 | ? _encode(String(u)) 79 | : _encode(String(u)).replace(/[+\/]/g, function(m0) { 80 | return m0 == '+' ? '-' : '_'; 81 | }).replace(/=/g, ''); 82 | }; 83 | var encodeURI = function(u) { return encode(u, true) }; 84 | // decoder stuff 85 | var re_btou = new RegExp([ 86 | '[\xC0-\xDF][\x80-\xBF]', 87 | '[\xE0-\xEF][\x80-\xBF]{2}', 88 | '[\xF0-\xF7][\x80-\xBF]{3}' 89 | ].join('|'), 'g'); 90 | var cb_btou = function(cccc) { 91 | switch(cccc.length) { 92 | case 4: 93 | var cp = ((0x07 & cccc.charCodeAt(0)) << 18) 94 | | ((0x3f & cccc.charCodeAt(1)) << 12) 95 | | ((0x3f & cccc.charCodeAt(2)) << 6) 96 | | (0x3f & cccc.charCodeAt(3)), 97 | offset = cp - 0x10000; 98 | return (fromCharCode((offset >>> 10) + 0xD800) 99 | + fromCharCode((offset & 0x3FF) + 0xDC00)); 100 | case 3: 101 | return fromCharCode( 102 | ((0x0f & cccc.charCodeAt(0)) << 12) 103 | | ((0x3f & cccc.charCodeAt(1)) << 6) 104 | | (0x3f & cccc.charCodeAt(2)) 105 | ); 106 | default: 107 | return fromCharCode( 108 | ((0x1f & cccc.charCodeAt(0)) << 6) 109 | | (0x3f & cccc.charCodeAt(1)) 110 | ); 111 | } 112 | }; 113 | var btou = function(b) { 114 | return b.replace(re_btou, cb_btou); 115 | }; 116 | var cb_decode = function(cccc) { 117 | var len = cccc.length, 118 | padlen = len % 4, 119 | n = (len > 0 ? b64tab[cccc.charAt(0)] << 18 : 0) 120 | | (len > 1 ? b64tab[cccc.charAt(1)] << 12 : 0) 121 | | (len > 2 ? b64tab[cccc.charAt(2)] << 6 : 0) 122 | | (len > 3 ? b64tab[cccc.charAt(3)] : 0), 123 | chars = [ 124 | fromCharCode( n >>> 16), 125 | fromCharCode((n >>> 8) & 0xff), 126 | fromCharCode( n & 0xff) 127 | ]; 128 | chars.length -= [0, 0, 2, 1][padlen]; 129 | return chars.join(''); 130 | }; 131 | var atob = global.atob ? function(a) { 132 | return global.atob(a); 133 | } : function(a){ 134 | return a.replace(/[\s\S]{1,4}/g, cb_decode); 135 | }; 136 | var _decode = buffer ? function(a) { 137 | return (a.constructor === buffer.constructor 138 | ? a : new buffer(a, 'base64')).toString(); 139 | } 140 | : function(a) { return btou(atob(a)) }; 141 | var decode = function(a){ 142 | return _decode( 143 | String(a).replace(/[-_]/g, function(m0) { return m0 == '-' ? '+' : '/' }) 144 | .replace(/[^A-Za-z0-9\+\/]/g, '') 145 | ); 146 | }; 147 | var noConflict = function() { 148 | var Base64 = global.Base64; 149 | global.Base64 = _Base64; 150 | return Base64; 151 | }; 152 | // export Base64 153 | var Base64 = { 154 | VERSION: version, 155 | atob: atob, 156 | btoa: btoa, 157 | fromBase64: decode, 158 | toBase64: encode, 159 | utob: utob, 160 | encode: encode, 161 | encodeURI: encodeURI, 162 | btou: btou, 163 | decode: decode, 164 | noConflict: noConflict 165 | }; 166 | return Base64; 167 | })(); 168 | 169 | module.exports = Base64; 170 | 171 | -------------------------------------------------------------------------------- /demo/lib/cos-auth.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";function t(t){return encodeURIComponent(t).replace(/!/g,"%21").replace(/'/g,"%27").replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/\*/g,"%2A")}var e=e||function(t,e){var n={},r=n.lib={},i=function(){},s=r.Base={extend:function(t){i.prototype=this;var e=new i;return t&&e.mixIn(t),e.hasOwnProperty("init")||(e.init=function(){e.$super.init.apply(this,arguments)}),e.init.prototype=e,e.$super=this,e},create:function(){var t=this.extend();return t.init.apply(t,arguments),t},init:function(){},mixIn:function(t){for(var e in t)t.hasOwnProperty(e)&&(this[e]=t[e]);t.hasOwnProperty("toString")&&(this.toString=t.toString)},clone:function(){return this.init.prototype.extend(this)}},o=r.WordArray=s.extend({init:function(t,e){t=this.words=t||[],this.sigBytes=void 0!=e?e:4*t.length},toString:function(t){return(t||c).stringify(this)},concat:function(t){var e=this.words,n=t.words,r=this.sigBytes;if(t=t.sigBytes,this.clamp(),r%4)for(var i=0;i>>2]|=(n[i>>>2]>>>24-i%4*8&255)<<24-(r+i)%4*8;else if(65535>>2]=n[i>>>2];else e.push.apply(e,n);return this.sigBytes+=t,this},clamp:function(){var e=this.words,n=this.sigBytes;e[n>>>2]&=4294967295<<32-n%4*8,e.length=t.ceil(n/4)},clone:function(){var t=s.clone.call(this);return t.words=this.words.slice(0),t},random:function(e){for(var n=[],r=0;r>>2]>>>24-r%4*8&255;n.push((i>>>4).toString(16)),n.push((15&i).toString(16))}return n.join("")},parse:function(t){for(var e=t.length,n=[],r=0;r>>3]|=parseInt(t.substr(r,2),16)<<24-r%8*4;return new o.init(n,e/2)}},h=a.Latin1={stringify:function(t){var e=t.words;t=t.sigBytes;for(var n=[],r=0;r>>2]>>>24-r%4*8&255));return n.join("")},parse:function(t){for(var e=t.length,n=[],r=0;r>>2]|=(255&t.charCodeAt(r))<<24-r%4*8;return new o.init(n,e)}},u=a.Utf8={stringify:function(t){try{return decodeURIComponent(escape(h.stringify(t)))}catch(t){throw Error("Malformed UTF-8 data")}},parse:function(t){return h.parse(unescape(encodeURIComponent(t)))}},f=r.BufferedBlockAlgorithm=s.extend({reset:function(){this._data=new o.init,this._nDataBytes=0},_append:function(t){"string"==typeof t&&(t=u.parse(t)),this._data.concat(t),this._nDataBytes+=t.sigBytes},_process:function(e){var n=this._data,r=n.words,i=n.sigBytes,s=this.blockSize,a=i/(4*s),a=e?t.ceil(a):t.max((0|a)-this._minBufferSize,0);if(e=a*s,i=t.min(4*e,i),e){for(var c=0;ch;h++){if(16>h)s[h]=0|t[e+h];else{var u=s[h-3]^s[h-8]^s[h-14]^s[h-16];s[h]=u<<1|u>>>31}u=(r<<5|r>>>27)+c+s[h],u=20>h?u+(1518500249+(i&o|~i&a)):40>h?u+(1859775393+(i^o^a)):60>h?u+((i&o|i&a|o&a)-1894007588):u+((i^o^a)-899497514),c=a,a=o,o=i<<30|i>>>2,i=r,r=u}n[0]=n[0]+r|0,n[1]=n[1]+i|0,n[2]=n[2]+o|0,n[3]=n[3]+a|0,n[4]=n[4]+c|0},_doFinalize:function(){var t=this._data,e=t.words,n=8*this._nDataBytes,r=8*t.sigBytes;return e[r>>>5]|=128<<24-r%32,e[14+(r+64>>>9<<4)]=Math.floor(n/4294967296),e[15+(r+64>>>9<<4)]=n,t.sigBytes=4*e.length,this._process(),this._hash},clone:function(){var t=i.clone.call(this);return t._hash=this._hash.clone(),t}});t.SHA1=i._createHelper(n),t.HmacSHA1=i._createHmacHelper(n)}(),function(){var t=e,n=t.enc.Utf8;t.algo.HMAC=t.lib.Base.extend({init:function(t,e){t=this._hasher=new t.init,"string"==typeof e&&(e=n.parse(e));var r=t.blockSize,i=4*r;e.sigBytes>i&&(e=t.finalize(e)),e.clamp();for(var s=this._oKey=e.clone(),o=this._iKey=e.clone(),a=s.words,c=o.words,h=0;h>>2]>>>24-s%4*8&255,a=e[s+1>>>2]>>>24-(s+1)%4*8&255,c=e[s+2>>>2]>>>24-(s+2)%4*8&255,h=o<<16|a<<8|c,u=0;u<4&&s+.75*u>>6*(3-u)&63));var f=r.charAt(64);if(f)for(;i.length%4;)i.push(f);return i.join("")},parse:function(t){var e=t.length,n=this._map,i=n.charAt(64);if(i){var s=t.indexOf(i);-1!=s&&(e=s)}for(var o=[],a=0,c=0;c>>6-c%4*2;o[a>>>2]|=(h|u)<<24-a%4*8,a++}return r.create(o,a)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}}();var n=function(t){var n=t.Pathname||"/",r=t.Expires,i="",s="",o=t.Bucket.match(/^(.+)-(\d+)$/);o&&(i=o[1],s=o[2]);var a=parseInt(Math.random()*Math.pow(2,32)),c=parseInt(Date.now()/1e3),h=c+(void 0===r?900:1*r||0),u="/"+s+"/"+i+encodeURIComponent(n).replace(/%2F/g,"/"),f="a="+s+"&b="+i+"&k="+t.SecretId+"&e="+h+"&t="+c+"&r="+a+"&f="+u,l=e.HmacSHA1(f,t.SecretKey),p=e.enc.Utf8.parse(f);return l.concat(p).toString(e.enc.Base64)},r=function(r){if(!r.SecretId)return console.error("missing param SecretId");if(!r.SecretKey)return console.error("missing param SecretKey");if("4.0"===r.Version)return n(r);r=r||{};var i=r.SecretId,s=r.SecretKey,o=(r.Method||"get").toLowerCase(),a=r.Query||{},c=r.Headers||{},h=r.Pathname||"/",u=r.Expires,f=function(t){var e=[];for(var n in t)t.hasOwnProperty(n)&&e.push(n);return e.sort(function(t,e){return t=t.toLowerCase(),e=e.toLowerCase(),t===e?0:t>e?1:-1})},l=function(e){var n,r,i,s=[],o=f(e);for(n=0;n ('000000' + x).slice(-(n || 2)); 43 | var d = new Date(); 44 | var ymd = d.getFullYear() + N(d.getMonth() + 1) + N(d.getDate()); 45 | var hms = N(d.getHours()) + N(d.getMinutes()) + N(d.getSeconds()); 46 | var r = N(Math.round((Math.random() * 1000000)), 6); 47 | var fileName = ymd + '_' + hms + '_' + r; 48 | if (ext) fileName += '.' + ext; 49 | return fileName; 50 | }; 51 | 52 | // 创建临时密钥服务 53 | var app = express(); 54 | app.use(bodyParser.json()); 55 | 56 | // 格式一:临时密钥接口 57 | app.all('/sts', function (req, res, next) { 58 | 59 | // TODO 这里根据自己业务需要做好放行判断 60 | if (config.allowPrefix === '_ALLOW_DIR_/*') { 61 | res.send({error: '请修改 allowPrefix 配置项,指定允许上传的路径前缀'}); 62 | return; 63 | } 64 | 65 | // 获取临时密钥 66 | var LongBucketName = config.bucket; 67 | var ShortBucketName = LongBucketName.substr(0, LongBucketName.lastIndexOf('-')); 68 | var AppId = LongBucketName.substr(LongBucketName.lastIndexOf('-') + 1); 69 | var policy = { 70 | 'version': '2.0', 71 | 'statement': [{ 72 | 'action': config.allowActions, 73 | 'effect': 'allow', 74 | 'resource': [ 75 | 'qcs::cos:' + config.region + ':uid/' + AppId + ':prefix//' + AppId + '/' + ShortBucketName + '/' + config.allowPrefix, 76 | ], 77 | }], 78 | }; 79 | var startTime = Math.round(Date.now() / 1000); 80 | STS.getCredential({ 81 | secretId: config.secretId, 82 | secretKey: config.secretKey, 83 | proxy: config.proxy, 84 | region: config.region, 85 | durationSeconds: config.durationSeconds, 86 | policy: policy, 87 | }, function (err, tempKeys) { 88 | if (tempKeys) tempKeys.startTime = startTime; 89 | res.send(err || tempKeys); 90 | }); 91 | }); 92 | 93 | 94 | // // 格式二:临时密钥接口,支持细粒度权限控制 95 | // // 判断是否允许获取密钥 96 | // var allowScope = function (scope) { 97 | // var allow = (scope || []).every(function (item) { 98 | // return config.allowActions.includes(item.action) && 99 | // item.bucket === config.bucket && 100 | // item.region === config.region && 101 | // (item.prefix || '').startsWith(config.allowPrefix); 102 | // }); 103 | // return allow; 104 | // }; 105 | // app.all('/sts-scope', function (req, res, next) { 106 | // var scope = req.body; 107 | // 108 | // // TODO 这里根据自己业务需要做好放行判断 109 | // if (config.allowPrefix === '_ALLOW_DIR_/*') { 110 | // res.send({error: '请修改 allowPrefix 配置项,指定允许上传的路径前缀'}); 111 | // return; 112 | // } 113 | // // TODO 这里可以判断 scope 细粒度控制权限 114 | // if (!scope || !scope.length || !allowScope(scope)) return res.send({error: 'deny'}); 115 | // 116 | // // 获取临时密钥 117 | // var policy = STS.getPolicy(scope); 118 | // var startTime = Math.round(Date.now() / 1000); 119 | // STS.getCredential({ 120 | // secretId: config.secretId, 121 | // secretKey: config.secretKey, 122 | // proxy: config.proxy, 123 | // durationSeconds: config.durationSeconds, 124 | // policy: policy, 125 | // }, function (err, tempKeys) { 126 | // if (tempKeys) tempKeys.startTime = startTime; 127 | // res.send(err || tempKeys); 128 | // }); 129 | // }); 130 | // 131 | // // 用于 PostObject 签名保护 132 | // app.all('/post-policy', function (req, res, next) { 133 | // var query = req.query; 134 | // var ext = query.ext; 135 | // var key = 'images/' + createFileName(ext); 136 | // var now = Math.round(Date.now() / 1000); 137 | // var exp = now + 900; 138 | // var qKeyTime = now + ';' + exp; 139 | // var qSignAlgorithm = 'sha1'; 140 | // var policy = JSON.stringify({ 141 | // 'expiration': new Date(exp * 1000).toISOString(), 142 | // 'conditions': [ 143 | // // {'acl': query.ACL}, 144 | // // ['starts-with', '$Content-Type', 'image/'], 145 | // // ['starts-with', '$success_action_redirect', redirectUrl], 146 | // // ['eq', '$x-cos-server-side-encryption', 'AES256'], 147 | // {'q-sign-algorithm': qSignAlgorithm}, 148 | // {'q-ak': config.secretId}, 149 | // {'q-sign-time': qKeyTime}, 150 | // {'bucket': config.bucket}, 151 | // {'key': key}, 152 | // ] 153 | // }); 154 | // 155 | // // 签名算法说明文档:https://www.qcloud.com/document/product/436/7778 156 | // // 步骤一:生成 SignKey 157 | // var signKey = crypto.createHmac('sha1', config.secretKey).update(qKeyTime).digest('hex'); 158 | // 159 | // // 步骤二:生成 StringToSign 160 | // var stringToSign = crypto.createHash('sha1').update(policy).digest('hex'); 161 | // 162 | // // 步骤三:生成 Signature 163 | // var qSignature = crypto.createHmac('sha1', signKey).update(stringToSign).digest('hex'); 164 | // 165 | // console.log(policy); 166 | // res.send({ 167 | // bucket: config.bucket, 168 | // region: config.region, 169 | // key: key, 170 | // policyObj: JSON.parse(policy), 171 | // policy: Buffer.from(policy).toString('base64'), 172 | // qSignAlgorithm: qSignAlgorithm, 173 | // qAk: config.secretId, 174 | // qKeyTime: qKeyTime, 175 | // qSignature: qSignature, 176 | // // securityToken: securityToken, // 如果使用临时密钥,要返回在这个资源 sessionToken 的值 177 | // }); 178 | // }); 179 | 180 | app.all('*', function (req, res, next) { 181 | res.send({code: -1, message: '404 Not Found'}); 182 | }); 183 | 184 | // 启动签名服务 185 | app.listen(3000); 186 | console.log('app is listening at http://127.0.0.1:3000'); 187 | -------------------------------------------------------------------------------- /demo/ciDemo/audit.js: -------------------------------------------------------------------------------- 1 | const COS = require('../lib/cos-wx-sdk-v5'); 2 | const config = require('../config'); 3 | const { cos, requestCallback } = require('../tools'); 4 | 5 | const auditDao = { 6 | '图片同步审核 getImageAuditing': getImageAuditing, 7 | '图片批量审核 postImagesAuditing': postImagesAuditing, 8 | '查询图片审核任务结果 getImageAuditingResult': getImageAuditingResult, 9 | '提交视频审核任务 postVideoAuditing': postVideoAuditing, 10 | '查询视频审核任务结果 getVideoAuditingResult': getVideoAuditingResult, 11 | '提交音频审核任务 postAudioAuditing': postAudioAuditing, 12 | '查询音频审核任务结果 getAudioAuditingResult': getAudioAuditingResult, 13 | '提交文本审核任务 postTextAuditing': postTextAuditing, 14 | '提交直播审核任务 postLiveAuditing': postLiveAuditing, 15 | '查询直播审核任务结果 getLiveAuditingResult': getLiveAuditingResult, 16 | }; 17 | 18 | function getImageAuditing() { 19 | cos.request( 20 | { 21 | Bucket: config.Bucket, 22 | Region: config.Region, 23 | Method: 'GET', 24 | Key: '1.png', 25 | Query: { 26 | 'ci-process': 'sensitive-content-recognition' /** 固定值,必须 */, 27 | 'biz-type': '' /** 审核类型,非必须 */, 28 | 'detect-url': '' /** 审核任意公网可访问的图片链接,非必须,与Key二选一传递 */, 29 | interval: 5 /** 审核 GIF 动图时,每隔interval帧截取一帧,非必须 */, 30 | 'max-frames': 5 /** 审核 GIF 动图时,最大截帧数,非必须 */, 31 | 'large-image-detect': '0' /** 是否需要压缩图片后再审核,非必须 */, 32 | }, 33 | }, 34 | function (err, data) { 35 | console.log(err || data); 36 | } 37 | ); 38 | } 39 | 40 | function postImagesAuditing() { 41 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com'; 42 | var url = 'https://' + host + '/image/auditing'; 43 | var body = COS.util.json2xml({ 44 | Request: { 45 | Input: [ 46 | { 47 | Object: '1.png', 48 | }, 49 | { 50 | Object: '6.png', 51 | }, 52 | ], 53 | Conf: { 54 | BizType: '', 55 | }, 56 | }, 57 | }); 58 | cos.request( 59 | { 60 | Bucket: config.Bucket, 61 | Region: config.Region, 62 | Method: 'POST', 63 | Url: url, 64 | Key: '/image/auditing', 65 | ContentType: 'application/xml', 66 | Body: body, 67 | }, 68 | function (err, data) { 69 | console.log(err || data); 70 | } 71 | ); 72 | } 73 | 74 | function getImageAuditingResult() { 75 | var jobId = 'si8263213daf3711eca0d1525400d88xxx'; // jobId可以通过图片批量审核返回 76 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com'; 77 | var url = 'https://' + host + '/image/auditing/' + jobId; 78 | cos.request( 79 | { 80 | Bucket: config.Bucket, 81 | Region: config.Region, 82 | Method: 'GET', 83 | Key: '/image/auditing/' + jobId, 84 | Url: url, 85 | }, 86 | function (err, data) { 87 | console.log(err || data); 88 | } 89 | ); 90 | } 91 | 92 | function postVideoAuditing() { 93 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com'; 94 | var url = 'https://' + host + '/video/auditing'; 95 | var body = COS.util.json2xml({ 96 | Request: { 97 | Input: { 98 | Object: '1.mp4', 99 | }, 100 | Conf: { 101 | BizType: '', 102 | Snapshot: { 103 | Count: 1000, // 视频截帧数量 104 | }, 105 | DetectContent: 1, // 是否审核视频声音,0-只审核视频不审核声音;1-审核视频+声音 106 | }, 107 | }, 108 | }); 109 | cos.request( 110 | { 111 | Bucket: config.Bucket, 112 | Region: config.Region, 113 | Method: 'POST', 114 | Url: url, 115 | Key: '/video/auditing', 116 | ContentType: 'application/xml', 117 | Body: body, 118 | }, 119 | function (err, data) { 120 | console.log(err || data); 121 | } 122 | ); 123 | } 124 | 125 | function getVideoAuditingResult() { 126 | var jobId = 'av5bd873d9f39011ecb6c95254009a49da'; // jobId可以通过提交视频审核任务返回 127 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com'; 128 | var url = 'https://' + host + '/video/auditing/' + jobId; 129 | cos.request( 130 | { 131 | Bucket: config.Bucket, 132 | Region: config.Region, 133 | Method: 'GET', 134 | Key: '/video/auditing/' + jobId, 135 | Url: url, 136 | }, 137 | function (err, data) { 138 | console.log(err || data); 139 | } 140 | ); 141 | } 142 | 143 | function postAudioAuditing() { 144 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com'; 145 | var url = 'https://' + host + '/audio/auditing'; 146 | var body = COS.util.json2xml({ 147 | Request: { 148 | Input: { 149 | Object: '1.mp3', 150 | }, 151 | Conf: { 152 | BizType: '', 153 | }, 154 | }, 155 | }); 156 | cos.request( 157 | { 158 | Bucket: config.Bucket, 159 | Region: config.Region, 160 | Method: 'POST', 161 | Url: url, 162 | Key: '/audio/auditing', 163 | ContentType: 'application/xml', 164 | Body: body, 165 | }, 166 | function (err, data) { 167 | console.log(err || data); 168 | } 169 | ); 170 | } 171 | 172 | function getAudioAuditingResult() { 173 | var jobId = 'sa0c28d41daff411ecb23352540078cxxx'; // jobId可以通过提交音频审核任务返回 174 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com'; 175 | var url = 'https://' + host + '/audio/auditing/' + jobId; 176 | cos.request( 177 | { 178 | Bucket: config.Bucket, 179 | Region: config.Region, 180 | Method: 'GET', 181 | Key: '/audio/auditing/' + jobId, 182 | Url: url, 183 | }, 184 | function (err, data) { 185 | console.log(err || data); 186 | } 187 | ); 188 | } 189 | 190 | function postTextAuditing() { 191 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com'; 192 | var url = 'https://' + host + '/text/auditing'; 193 | var body = COS.util.json2xml({ 194 | Request: { 195 | Input: { 196 | Content: COS.util.encodeBase64('hello') /* 需要审核的文本内容 */, 197 | }, 198 | Conf: { 199 | BizType: '', 200 | }, 201 | }, 202 | }); 203 | cos.request( 204 | { 205 | Bucket: config.Bucket, 206 | Region: config.Region, 207 | Method: 'POST', 208 | Url: url, 209 | Key: '/text/auditing' /** 固定值,必须 */, 210 | ContentType: 'application/xml' /** 固定值,必须 */, 211 | Body: body, 212 | }, 213 | function (err, data) { 214 | console.log(err || data); 215 | } 216 | ); 217 | } 218 | 219 | function postLiveAuditing() { 220 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com'; 221 | var url = 'https://' + host + '/video/auditing'; 222 | var body = COS.util.json2xml({ 223 | Request: { 224 | Type: 'live_video', 225 | Input: { 226 | Url: 'rtmp://example.com/live/123', // 需要审核的直播流播放地址 227 | // DataId: '', 228 | // UserInfo: {}, 229 | }, 230 | Conf: { 231 | BizType: '', 232 | // Callback: 'https://callback.com', // 回调地址,非必须 233 | // CallbackType: 1, // 回调片段类型,非必须 234 | }, 235 | }, 236 | }); 237 | cos.request( 238 | { 239 | Bucket: config.Bucket, 240 | Region: config.Region, 241 | Method: 'POST', 242 | Url: url, 243 | Key: '/video/auditing', 244 | ContentType: 'application/xml', 245 | Body: body, 246 | }, 247 | function (err, data) { 248 | console.log(err || data); 249 | } 250 | ); 251 | } 252 | 253 | function getLiveAuditingResult() { 254 | var jobId = 'av2b14a74dbd9011edb05a52540084c0xx'; // jobId可以通过提交直播审核任务返回 255 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com'; 256 | var url = 'https://' + host + '/video/auditing/' + jobId; 257 | cos.request( 258 | { 259 | Bucket: config.Bucket, 260 | Region: config.Region, 261 | Method: 'GET', 262 | Key: '/video/auditing/' + jobId, 263 | Url: url, 264 | }, 265 | function (err, data) { 266 | console.log(err || data); 267 | } 268 | ); 269 | } 270 | 271 | module.exports = auditDao; 272 | -------------------------------------------------------------------------------- /lib/crypto.js: -------------------------------------------------------------------------------- 1 | /* 2 | CryptoJS v3.1.2 3 | code.google.com/p/crypto-js 4 | (c) 2009-2013 by Jeff Mott. All rights reserved. 5 | code.google.com/p/crypto-js/wiki/License 6 | */ 7 | 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)}}, 8 | 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>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((f+b)%4);else if(65535>>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<< 9 | 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>>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>>3]|=parseInt(a.substr(f, 10 | 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>>2]>>>24-8*(f%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;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)))}}, 11 | 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;ka;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^ 15 | 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)})(); 16 | (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>> 2] >>> (24 - (i % 4) * 8)) & 0xff; 57 | var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff; 58 | var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff; 59 | 60 | var triplet = (byte1 << 16) | (byte2 << 8) | byte3; 61 | 62 | for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) { 63 | base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f)); 64 | } 65 | } 66 | 67 | // Add padding 68 | var paddingChar = map.charAt(64); 69 | if (paddingChar) { 70 | while (base64Chars.length % 4) { 71 | base64Chars.push(paddingChar); 72 | } 73 | } 74 | 75 | return base64Chars.join(''); 76 | }, 77 | 78 | /** 79 | * Converts a Base64 string to a word array. 80 | * 81 | * @param {string} base64Str The Base64 string. 82 | * 83 | * @return {WordArray} The word array. 84 | * 85 | * @static 86 | * 87 | * @example 88 | * 89 | * var wordArray = CryptoJS.enc.Base64.parse(base64String); 90 | */ 91 | parse: function (base64Str) { 92 | // Shortcuts 93 | var base64StrLength = base64Str.length; 94 | var map = this._map; 95 | 96 | // Ignore padding 97 | var paddingChar = map.charAt(64); 98 | if (paddingChar) { 99 | var paddingIndex = base64Str.indexOf(paddingChar); 100 | if (paddingIndex != -1) { 101 | base64StrLength = paddingIndex; 102 | } 103 | } 104 | 105 | // Convert 106 | var words = []; 107 | var nBytes = 0; 108 | for (var i = 0; i < base64StrLength; i++) { 109 | if (i % 4) { 110 | var bits1 = map.indexOf(base64Str.charAt(i - 1)) << ((i % 4) * 2); 111 | var bits2 = map.indexOf(base64Str.charAt(i)) >>> (6 - (i % 4) * 2); 112 | words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8); 113 | nBytes++; 114 | } 115 | } 116 | 117 | return WordArray.create(words, nBytes); 118 | }, 119 | 120 | _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' 121 | }; 122 | }()); 123 | 124 | module.exports = CryptoJS; 125 | -------------------------------------------------------------------------------- /demo/ciDemo/asr.js: -------------------------------------------------------------------------------- 1 | const COS = require('../lib/cos-wx-sdk-v5'); 2 | const config = require('../config'); 3 | const { cos, requestCallback } = require('../tools'); 4 | 5 | const asrDao = { 6 | '提交音频降噪任务 postNoiseReduction': postNoiseReduction, 7 | '提交人声分离任务 postVoiceSeparate': postVoiceSeparate, 8 | '提交语音合成任务 postTts': postTts, 9 | '提交语音识别任务 postSpeechRecognition': postSpeechRecognition, 10 | '查询语音识别队列 getAsrQueue': getAsrQueue, 11 | '更新语音识别队列 putAsrQueue': putAsrQueue, 12 | '查询语音识别开通状态 getAsrBucket': getAsrBucket, 13 | }; 14 | 15 | function postNoiseReduction() { 16 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com/jobs'; 17 | var url = 'https://' + host; 18 | var body = COS.util.json2xml({ 19 | Request: { 20 | Tag: 'NoiseReduction', 21 | Input: { 22 | Object: 'ci/music.mp3', // 文件名,取值为文件在当前存储桶中的完整名称 23 | }, 24 | Operation: { 25 | Output: { 26 | Bucket: config.Bucket, // 输出的存储桶 27 | Region: config.Region, // 输出的存储桶的地域 28 | Object: 'ci/out.mp3', // 输出的文件Key 29 | }, 30 | }, 31 | // QueueId: '', // 任务所在的队列 ID,非必须 32 | // CallBackFormat: '', // 任务回调格式,JSON 或 XML,默认 XML,优先级高于队列的回调格式,非必须 33 | // CallBackType: '', // 任务回调类型,Url 或 TDMQ,默认 Url,优先级高于队列的回调类型,非必须 34 | // CallBack: '', // 任务回调地址,优先级高于队列的回调地址。设置为 no 时,表示队列的回调地址不产生回调,非必须 35 | // CallBackMqConfig: '', // 任务回调 TDMQ 配置,当 CallBackType 为 TDMQ 时必填,非必须 36 | }, 37 | }); 38 | cos.request( 39 | { 40 | Method: 'POST', 41 | Key: 'jobs', 42 | Url: url, 43 | Body: body, 44 | ContentType: 'application/xml', 45 | }, 46 | function (err, data) { 47 | console.log(err || data); 48 | } 49 | ); 50 | } 51 | 52 | function postVoiceSeparate() { 53 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com/jobs'; 54 | var url = 'https://' + host; 55 | var body = COS.util.json2xml({ 56 | Request: { 57 | Tag: 'VoiceSeparate', 58 | Input: { 59 | Object: 'ci/music.mp3', // 文件名,取值为文件在当前存储桶中的完整名称 60 | }, 61 | Operation: { 62 | // VoiceSeparate: {}, // 指定转码模板参数,非必须 63 | TemplateId: 't13fca82ad97e84878a22cd81bd2e5652c', // 指定的模板 ID,必须 64 | // JobLevel: 0, // 任务优先级,级别限制:0 、1 、2。级别越大任务优先级越高,默认为0,非必须 65 | Output: { 66 | Bucket: config.Bucket, // 输出的存储桶 67 | Region: config.Region, // 输出的存储桶的地域 68 | Object: 'ci/out/background.mp3', // 输出的文件Key,背景音结果文件名,不能与 AuObject 同时为空 69 | AuObject: 'ci/out/audio.mp3', 70 | }, 71 | }, 72 | // QueueId: '', // 任务所在的队列 ID,非必须 73 | // CallBackFormat: '', // 任务回调格式,JSON 或 XML,默认 XML,优先级高于队列的回调格式,非必须 74 | // CallBackType: '', // 任务回调类型,Url 或 TDMQ,默认 Url,优先级高于队列的回调类型,非必须 75 | // CallBack: '', // 任务回调地址,优先级高于队列的回调地址。设置为 no 时,表示队列的回调地址不产生回调,非必须 76 | // CallBackMqConfig: '', // 任务回调 TDMQ 配置,当 CallBackType 为 TDMQ 时必填,非必须 77 | }, 78 | }); 79 | cos.request( 80 | { 81 | Method: 'POST', 82 | Key: 'jobs', 83 | Url: url, 84 | Body: body, 85 | ContentType: 'application/xml', 86 | }, 87 | function (err, data) { 88 | console.log(err || data); 89 | } 90 | ); 91 | } 92 | 93 | function postTts() { 94 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com/jobs'; 95 | var url = 'https://' + host; 96 | var body = COS.util.json2xml({ 97 | Request: { 98 | Tag: 'Tts', 99 | Operation: { 100 | // VoiceSeparate: {}, // 指定转码模板参数,非必须 101 | TemplateId: 't192931b3564084168a3f50ebfea59acb3', // 指定的模板 ID,必须 102 | // JobLevel: 0, // 任务优先级,级别限制:0 、1 、2。级别越大任务优先级越高,默认为0,非必须 103 | TtsConfig: { 104 | InputType: 'Text', 105 | Input: '床前明月光,疑是地上霜', 106 | }, 107 | Output: { 108 | Bucket: config.Bucket, // 输出的存储桶 109 | Region: config.Region, // 输出的存储桶的地域 110 | Object: 'ci/out/tts.mp3', // 输出的文件Key 111 | }, 112 | }, 113 | // QueueId: '', // 任务所在的队列 ID,非必须 114 | // CallBackFormat: '', // 任务回调格式,JSON 或 XML,默认 XML,优先级高于队列的回调格式,非必须 115 | // CallBackType: '', // 任务回调类型,Url 或 TDMQ,默认 Url,优先级高于队列的回调类型,非必须 116 | // CallBack: '', // 任务回调地址,优先级高于队列的回调地址。设置为 no 时,表示队列的回调地址不产生回调,非必须 117 | // CallBackMqConfig: '', // 任务回调 TDMQ 配置,当 CallBackType 为 TDMQ 时必填,非必须 118 | }, 119 | }); 120 | cos.request( 121 | { 122 | Method: 'POST', 123 | Key: 'jobs', 124 | Url: url, 125 | Body: body, 126 | ContentType: 'application/xml', 127 | }, 128 | function (err, data) { 129 | console.log(err || data); 130 | } 131 | ); 132 | } 133 | 134 | function postSpeechRecognition() { 135 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com/asr_jobs'; 136 | var url = 'https://' + host; 137 | var body = COS.util.json2xml({ 138 | Request: { 139 | Tag: 'SpeechRecognition', 140 | Input: { 141 | Object: 'ci/music.mp3', // 文件名,取值为文件在当前存储桶中的完整名称,与Url参数二选一 142 | // Url: 'http://examplebucket-1250000000.cos.ap-shanghai.myqcloud.com/music.mp3', // 病毒文件的链接地址,与Object参数二选一 143 | }, 144 | Operation: { 145 | SpeechRecognition: { 146 | EngineModelType: '16k_zh_video', // 引擎模型类型 147 | ChannelNum: 1, // 语音声道数 148 | ResTextFormat: 0, // 识别结果返回形式 149 | FilterDirty: 1, // 是否过滤脏词(目前支持中文普通话引擎) 150 | FilterModal: 1, // 是否过语气词(目前支持中文普通话引擎) 151 | ConvertNumMode: 0, // 是否进行阿拉伯数字智能转换(目前支持中文普通话引擎) 152 | }, 153 | Output: { 154 | Bucket: config.Bucket, // 输出的存储桶 155 | Region: config.Region, // 输出的存储桶的地域 156 | Object: 'ci/out/SpeechRecognition.mp3', // 输出的文件Key 157 | }, 158 | }, 159 | // QueueId: '', // 任务所在的队列 ID,非必须 160 | // CallBackFormat: '', // 任务回调格式,JSON 或 XML,默认 XML,优先级高于队列的回调格式,非必须 161 | // CallBackType: '', // 任务回调类型,Url 或 TDMQ,默认 Url,优先级高于队列的回调类型,非必须 162 | // CallBack: '', // 任务回调地址,优先级高于队列的回调地址。设置为 no 时,表示队列的回调地址不产生回调,非必须 163 | // CallBackMqConfig: '', // 任务回调 TDMQ 配置,当 CallBackType 为 TDMQ 时必填,非必须 164 | }, 165 | }); 166 | cos.request( 167 | { 168 | Method: 'POST', 169 | Key: 'asr_jobs', 170 | Url: url, 171 | Body: body, 172 | ContentType: 'application/xml', 173 | }, 174 | function (err, data) { 175 | console.log(err || data); 176 | } 177 | ); 178 | } 179 | 180 | function getAsrQueue() { 181 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com/asrqueue'; 182 | var url = 'https://' + host; 183 | cos.request( 184 | { 185 | Method: 'GET', 186 | Key: 'asrqueue', 187 | Url: url, 188 | Query: { 189 | // queueIds: '', /* 非必须,队列 ID,以“,”符号分割字符串 */ 190 | // state: '', /* 非必须,1=Active,2=Paused */ 191 | // pageNumber: 1, /* 非必须,第几页 */ 192 | // pageSize: 2, /* 非必须,每页个数 */ 193 | }, 194 | }, 195 | function (err, data) { 196 | console.log(err || data); 197 | } 198 | ); 199 | } 200 | 201 | function putAsrQueue() { 202 | // 任务所在的队列 ID,请使用查询队列(https://cloud.tencent.com/document/product/460/46234)获取或前往万象控制台(https://cloud.tencent.com/document/product/460/46487)在存储桶中查询 203 | var queueId = 'pcc77499e85c311edb9865254008618d9'; 204 | var host = config.Bucket + '.ci.' + config.Region + '.myqcloud.com/asrqueue/' + queueId; 205 | var url = 'https://' + host; 206 | var body = COS.util.json2xml({ 207 | Request: { 208 | Name: 'queue-doc-process-1', 209 | QueueID: queueId, 210 | State: 'Paused', 211 | NotifyConfig: { 212 | // Url: '', 213 | // Type: 'Url', 214 | // Event: '', 215 | State: 'Off', 216 | }, 217 | }, 218 | }); 219 | cos.request( 220 | { 221 | Method: 'PUT', 222 | Key: 'asrqueue/' + queueId, 223 | Url: url, 224 | Body: body, 225 | ContentType: 'application/xml', 226 | }, 227 | function (err, data) { 228 | console.log(err || data); 229 | } 230 | ); 231 | } 232 | 233 | function getAsrBucket() { 234 | var host = 'ci.' + config.Region + '.myqcloud.com/asrbucket'; 235 | var url = 'https://' + host; 236 | cos.request( 237 | { 238 | Method: 'GET', 239 | Key: 'asrbucket', 240 | Url: url, 241 | Query: { 242 | // regions: '', /* 非必须,地域信息,以“,”分隔字符串,支持 All、ap-shanghai、ap-beijing */ 243 | // bucketNames: '', /* 非必须,存储桶名称,以“,”分隔,支持多个存储桶,精确搜索 */ 244 | // bucketName: '', /* 非必须,存储桶名称前缀,前缀搜索 */ 245 | // pageNumber: 1, /* 非必须,第几页 */ 246 | // pageSize: 10, /* 非必须,每页个数 */ 247 | }, 248 | }, 249 | function (err, data) { 250 | console.log(err || data); 251 | } 252 | ); 253 | } 254 | 255 | module.exports = asrDao; 256 | -------------------------------------------------------------------------------- /src/task.js: -------------------------------------------------------------------------------- 1 | var session = require('./session'); 2 | var util = require('./util'); 3 | 4 | var originApiMap = {}; 5 | var transferToTaskMethod = function (apiMap, apiName) { 6 | originApiMap[apiName] = apiMap[apiName]; 7 | apiMap[apiName] = function (params, callback) { 8 | if (params.SkipTask) { 9 | originApiMap[apiName].call(this, params, callback); 10 | } else { 11 | this._addTask(apiName, params, callback); 12 | } 13 | }; 14 | }; 15 | 16 | var initTask = function (cos) { 17 | var queue = []; 18 | var tasks = {}; 19 | var uploadingFileCount = 0; 20 | var nextUploadIndex = 0; 21 | 22 | // 接口返回简略的任务信息 23 | var formatTask = function (task) { 24 | var t = { 25 | id: task.id, 26 | Bucket: task.Bucket, 27 | Region: task.Region, 28 | Key: task.Key, 29 | FilePath: task.FilePath, 30 | state: task.state, 31 | loaded: task.loaded, 32 | size: task.size, 33 | speed: task.speed, 34 | percent: task.percent, 35 | hashPercent: task.hashPercent, 36 | error: task.error, 37 | }; 38 | if (task.FilePath) t.FilePath = task.FilePath; 39 | return t; 40 | }; 41 | 42 | var emitListUpdate = (function () { 43 | var timer; 44 | var emit = function () { 45 | timer = 0; 46 | cos.emit('task-list-update', { list: util.map(queue, formatTask) }); 47 | cos.emit('list-update', { list: util.map(queue, formatTask) }); 48 | }; 49 | return function () { 50 | if (!timer) timer = setTimeout(emit); 51 | }; 52 | })(); 53 | 54 | var clearQueue = function () { 55 | if (queue.length <= cos.options.UploadQueueSize) return; 56 | for ( 57 | var i = 0; 58 | i < nextUploadIndex && // 小于当前操作的 index 才清理 59 | i < queue.length && // 大于队列才清理 60 | queue.length > cos.options.UploadQueueSize; // 如果还太多,才继续清理 61 | 62 | ) { 63 | var isActive = queue[i].state === 'waiting' || queue[i].state === 'checking' || queue[i].state === 'uploading'; 64 | if (!queue[i] || !isActive) { 65 | tasks[queue[i].id] && delete tasks[queue[i].id]; 66 | queue.splice(i, 1); 67 | nextUploadIndex--; 68 | } else { 69 | i++; 70 | } 71 | } 72 | emitListUpdate(); 73 | }; 74 | 75 | var startNextTask = function () { 76 | // 检查是否允许增加执行进程 77 | if (uploadingFileCount >= cos.options.FileParallelLimit) return; 78 | // 跳过不可执行的任务 79 | while (queue[nextUploadIndex] && queue[nextUploadIndex].state !== 'waiting') nextUploadIndex++; 80 | // 检查是否已遍历结束 81 | if (nextUploadIndex >= queue.length) return; 82 | // 上传该遍历到的任务 83 | var task = queue[nextUploadIndex]; 84 | nextUploadIndex++; 85 | uploadingFileCount++; 86 | task.state = 'checking'; 87 | task.params.onTaskStart && task.params.onTaskStart(formatTask(task)); 88 | !task.params.UploadData && (task.params.UploadData = {}); 89 | var apiParams = util.formatParams(task.api, task.params); 90 | originApiMap[task.api].call(cos, apiParams, function (err, data) { 91 | if (!cos._isRunningTask(task.id)) return; 92 | if (task.state === 'checking' || task.state === 'uploading') { 93 | task.state = err ? 'error' : 'success'; 94 | err && (task.error = err); 95 | uploadingFileCount--; 96 | emitListUpdate(); 97 | startNextTask(); 98 | task.callback && task.callback(err, data); 99 | if (task.state === 'success') { 100 | if (task.params) { 101 | delete task.params.UploadData; 102 | delete task.params.Body; 103 | delete task.params; 104 | } 105 | delete task.callback; 106 | } 107 | } 108 | clearQueue(); 109 | }); 110 | emitListUpdate(); 111 | // 异步执行下一个任务 112 | setTimeout(startNextTask); 113 | }; 114 | 115 | var killTask = function (id, switchToState) { 116 | var task = tasks[id]; 117 | if (!task) return; 118 | var waiting = task && task.state === 'waiting'; 119 | var running = task && (task.state === 'checking' || task.state === 'uploading'); 120 | if ( 121 | (switchToState === 'canceled' && task.state !== 'canceled') || 122 | (switchToState === 'paused' && waiting) || 123 | (switchToState === 'paused' && running) 124 | ) { 125 | if (switchToState === 'paused' && task.params.Body && typeof task.params.Body.pipe === 'function') { 126 | console.error('stream not support pause'); 127 | return; 128 | } 129 | task.state = switchToState; 130 | cos.emit('inner-kill-task', { TaskId: id, toState: switchToState }); 131 | try { 132 | var UploadId = task && task.params && task.params.UploadData.UploadId; 133 | } catch (e) {} 134 | if (switchToState === 'canceled' && UploadId) session.removeUsing(UploadId); 135 | emitListUpdate(); 136 | if (running) { 137 | uploadingFileCount--; 138 | startNextTask(); 139 | } 140 | if (switchToState === 'canceled') { 141 | if (task.params) { 142 | delete task.params.UploadData; 143 | delete task.params.Body; 144 | delete task.params; 145 | } 146 | delete task.callback; 147 | } 148 | } 149 | clearQueue(); 150 | }; 151 | 152 | cos._addTasks = function (taskList) { 153 | util.each(taskList, function (task) { 154 | cos._addTask(task.api, task.params, task.callback, true); 155 | }); 156 | emitListUpdate(); 157 | }; 158 | 159 | cos._addTask = function (api, params, callback, ignoreAddEvent) { 160 | // 如果小程序版本不支持获取文件分片内容,统一转到 简单上传 接口上传 161 | var simpleUploadMethod = cos.options.SimpleUploadMethod === 'postObject' ? 'postObject' : 'putObject'; 162 | if (api === 'sliceUploadFile' && !util.canFileSlice()) api = simpleUploadMethod; 163 | 164 | // 复制参数对象 165 | params = util.formatParams(api, params); 166 | 167 | // 生成 id 168 | var id = util.uuid(); 169 | params.TaskId = id; 170 | params.onTaskReady && params.onTaskReady(id); 171 | 172 | var task = { 173 | // env 174 | params: params, 175 | callback: callback, 176 | api: api, 177 | index: queue.length, 178 | // task 179 | id: id, 180 | Bucket: params.Bucket, 181 | Region: params.Region, 182 | Key: params.Key, 183 | FilePath: params.FilePath || '', 184 | state: 'waiting', 185 | loaded: 0, 186 | size: 0, 187 | speed: 0, 188 | percent: 0, 189 | hashPercent: 0, 190 | error: null, 191 | }; 192 | var onHashProgress = params.onHashProgress; 193 | params.onHashProgress = function (info) { 194 | if (!cos._isRunningTask(task.id)) return; 195 | task.hashPercent = info.percent; 196 | onHashProgress && onHashProgress(info); 197 | emitListUpdate(); 198 | }; 199 | var onProgress = params.onProgress; 200 | params.onProgress = function (info) { 201 | if (!cos._isRunningTask(task.id)) return; 202 | task.state === 'checking' && (task.state = 'uploading'); 203 | task.loaded = info.loaded; 204 | task.size = info.total; 205 | task.speed = info.speed; 206 | task.percent = info.percent; 207 | onProgress && onProgress(info); 208 | emitListUpdate(); 209 | }; 210 | 211 | // 异步获取 filesize 212 | util.getFileSize(api, params, function (err, size) { 213 | // 开始处理上传 214 | if (err) { 215 | // 如果获取大小出错,不加入队列 216 | callback(err); 217 | return; 218 | } 219 | // 获取完文件大小再把任务加入队列 220 | tasks[id] = task; 221 | queue.push(task); 222 | task.size = size; 223 | !ignoreAddEvent && emitListUpdate(); 224 | startNextTask(); 225 | clearQueue(); 226 | }); 227 | return id; 228 | }; 229 | cos._isRunningTask = function (id) { 230 | var task = tasks[id]; 231 | return !!(task && (task.state === 'checking' || task.state === 'uploading')); 232 | }; 233 | cos.getTaskList = function () { 234 | return util.map(queue, formatTask); 235 | }; 236 | cos.cancelTask = function (id) { 237 | killTask(id, 'canceled'); 238 | }; 239 | cos.pauseTask = function (id) { 240 | killTask(id, 'paused'); 241 | }; 242 | cos.restartTask = function (id) { 243 | var task = tasks[id]; 244 | if (task && (task.state === 'paused' || task.state === 'error')) { 245 | task.state = 'waiting'; 246 | emitListUpdate(); 247 | nextUploadIndex = Math.min(nextUploadIndex, task.index); 248 | startNextTask(); 249 | } 250 | }; 251 | cos.isUploadRunning = function () { 252 | return uploadingFileCount || nextUploadIndex < queue.length; 253 | }; 254 | }; 255 | 256 | module.exports.transferToTaskMethod = transferToTaskMethod; 257 | module.exports.init = initTask; 258 | -------------------------------------------------------------------------------- /demo-album/pages/album/album.js: -------------------------------------------------------------------------------- 1 | const { listToMatrix } = require('../../lib/util.js'); 2 | var COS = require('../../lib/cos-wx-sdk-v5.js'); 3 | const config = require('../../config.js'); 4 | const util = require('../../lib/util.js'); 5 | 6 | 7 | var cos = new COS({ 8 | getAuthorization: function (options, callback) { 9 | wx.request({ 10 | method: 'GET', 11 | url: config.stsUrl, // 服务端签名,参考 server 目录下的两个签名例子 12 | dataType: 'json', 13 | success: function (result) { 14 | var data = result.data; 15 | callback({ 16 | TmpSecretId: data.credentials && data.credentials.tmpSecretId, 17 | TmpSecretKey: data.credentials && data.credentials.tmpSecretKey, 18 | XCosSecurityToken: data.credentials && data.credentials.sessionToken, 19 | ExpiredTime: data.expiredTime, 20 | }); 21 | } 22 | }); 23 | }, 24 | }); 25 | 26 | Page({ 27 | data: { 28 | // 相册列表数据 29 | albumList: [], 30 | 31 | // 图片布局列表(二维数组,由`albumList`计算而得) 32 | layoutList: [], 33 | 34 | // 布局列数 35 | layoutColumnSize: 3, 36 | 37 | // 是否显示loading 38 | showLoading: false, 39 | 40 | // loading提示语 41 | loadingMessage: '', 42 | 43 | // 是否显示toast 44 | showToast: false, 45 | 46 | // 提示消息 47 | toastMessage: '', 48 | 49 | // 是否显示动作命令 50 | showActionsSheet: false, 51 | 52 | // 当前操作的图片 53 | imageInAction: '', 54 | 55 | // 图片预览模式 56 | previewMode: false, 57 | 58 | // 当前预览索引 59 | previewIndex: 0, 60 | 61 | // 切换动画的时间 62 | slideDuration: 400, 63 | }, 64 | onShareAppMessage: function (res) { 65 | return { 66 | title: 'COS 上传示例 - 共享相册', 67 | path: this.route, 68 | } 69 | }, 70 | 71 | // 显示loading提示 72 | showLoading(loadingMessage) { 73 | this.setData({ showLoading: true, loadingMessage }); 74 | }, 75 | 76 | // 隐藏loading提示 77 | hideLoading() { 78 | this.setData({ showLoading: false, loadingMessage: '' }); 79 | }, 80 | 81 | // 显示toast消息 82 | showToast(toastMessage) { 83 | this.setData({ showToast: true, toastMessage }); 84 | }, 85 | 86 | // 隐藏toast消息 87 | hideToast() { 88 | this.setData({ showToast: false, toastMessage: '' }); 89 | }, 90 | 91 | // 隐藏动作列表 92 | hideActionSheet() { 93 | this.setData({ showActionsSheet: false, imageInAction: '' }); 94 | }, 95 | 96 | onLoad() { 97 | var self = this; 98 | this.renderAlbumList(); 99 | this.getAlbumList(function (list) { 100 | list = self.data.albumList.concat(list || {}); 101 | if (!list.length) { 102 | list = []; 103 | } 104 | list = list.reverse(); 105 | self.setData({ 'albumList': list }); 106 | self.renderAlbumList(); 107 | }); 108 | }, 109 | 110 | // 获取相册列表 111 | getAlbumList(callback) { 112 | this.showLoading('加载列表中…'); 113 | setTimeout(() => this.hideLoading(), 1000); 114 | cos.getBucket({ 115 | Bucket: config.Bucket, 116 | Region: config.Region, 117 | Prefix: config.albumDir 118 | }, function (err, data) { 119 | if (data) { 120 | var list = (data && data.Contents || []) 121 | .map((v) => 'https://' + config.Bucket + '.cos.' + config.Region + '.myqcloud.com/' + util.camSafeUrlEncode(v.Key).replace(/%2F/g, '/')) 122 | .filter((v) => /\.(jpg|png|gif)$/.test(v)); 123 | callback(list); 124 | } else { 125 | callback([]); 126 | } 127 | }); 128 | }, 129 | 130 | // 渲染相册列表 131 | renderAlbumList() { 132 | let layoutColumnSize = this.data.layoutColumnSize; 133 | let layoutList = []; 134 | var imageList = [].concat(this.data.albumList); 135 | imageList.unshift({type:'add'}); 136 | layoutList = listToMatrix(imageList, layoutColumnSize); 137 | this.setData({ layoutList }); 138 | }, 139 | 140 | // 从相册选择照片或拍摄照片 141 | chooseImage() { 142 | var self = this; 143 | wx.chooseImage({ 144 | count: 9, 145 | sizeType: ['original', 'compressed'], 146 | sourceType: ['album', 'camera'], 147 | success: (res) => { 148 | this.showLoading('正在上传图片…'); 149 | res.tempFilePaths.forEach(function (filePath) { 150 | var Key = config.albumDir + util.getRandFileName(filePath); 151 | filePath && cos.postObject({ 152 | Bucket: config.Bucket, 153 | Region: config.Region, 154 | Key: Key, 155 | FilePath: filePath 156 | }, function (err, data) { 157 | if (data) { 158 | let albumList = self.data.albumList; 159 | albumList.unshift('https://' + data.Location); 160 | self.setData({ albumList }); 161 | self.renderAlbumList(); 162 | } 163 | self.hideLoading(); 164 | }); 165 | }); 166 | }, 167 | }); 168 | }, 169 | 170 | // 进入预览模式 171 | enterPreviewMode(event) { 172 | var self = this; 173 | if (this.data.showActionsSheet) { 174 | return; 175 | } 176 | let imageUrl = event.target.dataset.src; 177 | let previewIndex = this.data.albumList.indexOf(imageUrl); 178 | 179 | this.setData({ slideDuration: 0 }); 180 | setTimeout(function () { 181 | self.setData({ previewMode: true, previewIndex: previewIndex}); 182 | setTimeout(function () { 183 | self.setData({ slideDuration: 400 }); 184 | }, 400); 185 | }); 186 | }, 187 | 188 | // 退出预览模式 189 | leavePreviewMode() { 190 | this.setData({ previewMode: false, previewIndex: 0 }); 191 | }, 192 | 193 | // 显示可操作命令 194 | showActions(event) { 195 | this.setData({ showActionsSheet: true, imageInAction: event.target.dataset.src }); 196 | }, 197 | 198 | // 下载图片 199 | downloadImage() { 200 | this.showLoading('正在保存图片…'); 201 | console.log('download_image_url', this.data.imageInAction); 202 | 203 | wx.downloadFile({ 204 | url: this.data.imageInAction, 205 | type: 'image', 206 | success: (resp) => { 207 | wx.saveFile({ 208 | tempFilePath: resp.tempFilePath, 209 | success: (resp) => { 210 | this.showToast('图片保存成功'); 211 | }, 212 | fail: (resp) => { 213 | console.log('fail', resp); 214 | }, 215 | complete: (resp) => { 216 | console.log('complete', resp); 217 | this.hideLoading(); 218 | }, 219 | }); 220 | }, 221 | 222 | fail: (resp) => { 223 | console.log('fail', resp); 224 | }, 225 | }); 226 | 227 | this.setData({ showActionsSheet: false, imageInAction: '' }); 228 | }, 229 | 230 | // 删除图片 231 | deleteImage() { 232 | let imageUrl = this.data.imageInAction; 233 | var m = imageUrl.match(/^https:\/\/[^\/]+\/([^#?]+)/); 234 | var Key = m && m[1] || ''; 235 | if (Key) { 236 | this.showLoading('正在删除图片…'); 237 | this.setData({ showActionsSheet: false, imageInAction: '' }); 238 | cos.deleteObject({ 239 | Bucket: config.Bucket, 240 | Region: config.Region, 241 | Key: Key, 242 | }, (err, data)=>{ 243 | if (data) { 244 | let index = this.data.albumList.indexOf(imageUrl); 245 | if (~index) { 246 | let albumList = this.data.albumList; 247 | albumList.splice(index, 1); 248 | this.setData({ albumList }); 249 | this.renderAlbumList(); 250 | } 251 | this.showToast('图片删除成功'); 252 | } else { 253 | this.showToast('图片删除失败'); 254 | } 255 | this.hideLoading(); 256 | }); 257 | } 258 | }, 259 | }); 260 | -------------------------------------------------------------------------------- /src/tracker.js: -------------------------------------------------------------------------------- 1 | const pkg = require('../package.json'); 2 | let beacon = null; 3 | 4 | const getBeacon = (Beacon, delay) => { 5 | if (!beacon) { 6 | // 生成 beacon 7 | if (typeof Beacon !== 'function') { 8 | throw new Error('Beacon not found'); 9 | } 10 | beacon = new Beacon({ 11 | appkey: '0WEB05PY6MHRGK0U', 12 | versionCode: pkg.version, 13 | channelID: 'mp_sdk', //渠道,选填 14 | openid: 'openid', // 用户id, 选填 15 | unionid: 'unid', //用户unionid , 类似idfv,选填 16 | strictMode: false, //严苛模式开关, 打开严苛模式会主动抛出异常, 上线请务必关闭!!! 17 | delay, // 普通事件延迟上报时间(单位毫秒), 默认1000(1秒),选填 18 | sessionDuration: 60 * 1000, // session变更的时间间隔, 一个用户持续30分钟(默认值)没有任何上报则算另一次 session,每变更一次session上报一次启动事件(rqd_applaunched),使用毫秒(ms),最小值30秒,选填 19 | }); 20 | } 21 | return beacon; 22 | }; 23 | 24 | // 毫秒转秒 25 | const ms2s = function (ms) { 26 | if (!ms || ms < 0) return 0; 27 | return (ms / 1000).toFixed(3); 28 | }; 29 | 30 | const utils = { 31 | // 生成uid 每个链路对应唯一一条uid 32 | getUid() { 33 | var S4 = function () { 34 | return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); 35 | }; 36 | return S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4(); 37 | }, 38 | // 获取网络类型 4g | wifi 39 | getNetType() { 40 | return new Promise((resolve) => { 41 | if (wx.canIUse('getNetworkType')) { 42 | try { 43 | wx.getNetworkType({ 44 | success(res) { 45 | resolve(res.networkType); 46 | }, 47 | }); 48 | } catch (e) { 49 | resolve('can_not_get_network_type'); 50 | } 51 | } else { 52 | resolve('can_not_get_network_type'); 53 | } 54 | }); 55 | }, 56 | // 获取系统信息 57 | getSystemInfo() { 58 | const baseInfo = { 59 | // ↓上报项 60 | devicePlatform: 'can_not_get_system_info', // ios/anroid/windows/mac/devtools 61 | wxVersion: 'can_not_get_system_info', 62 | wxSystem: 'can_not_get_system_info', 63 | wxSdkVersion: 'can_not_get_system_info', 64 | }; 65 | let appBaseInfo = {}; 66 | let deviceInfo = {}; 67 | if (wx.canIUse('getAppBaseInfo')) { 68 | appBaseInfo = wx.getAppBaseInfo() || {}; 69 | } 70 | if (wx.canIUse('getDeviceInfo')) { 71 | deviceInfo = wx.getDeviceInfo() || {}; 72 | } 73 | const sdkVersion = appBaseInfo.SDKVersion || 'can_not_get_system_info'; 74 | const version = appBaseInfo.version || 'can_not_get_system_info'; 75 | const platform = deviceInfo.platform || 'can_not_get_system_info'; 76 | const system = deviceInfo.system || 'can_not_get_system_info'; 77 | 78 | Object.assign(baseInfo, { 79 | devicePlatform: platform, 80 | wxVersion: version, 81 | wxSystem: system, 82 | wxSdkVersion: sdkVersion, 83 | }); 84 | return baseInfo; 85 | }, 86 | }; 87 | 88 | // 设备信息,只取一次值 89 | const deviceInfo = utils.getSystemInfo(); 90 | 91 | const transApiName = (api) => { 92 | if (['putObject', 'sliceUploadFile', 'uploadFile', 'uploadFiles'].includes(api)) { 93 | return 'UploadTask'; 94 | } else if (api === 'getObject') { 95 | return 'DownloadTask'; 96 | } else if (['putObjectCopy', 'sliceCopyFile'].includes(api)) { 97 | return 'CopyTask'; 98 | } 99 | return api; 100 | }; 101 | 102 | // 上报参数驼峰改下划线 103 | function camel2underline(key) { 104 | return key.replace(/([A-Z])/g, '_$1').toLowerCase(); 105 | } 106 | function formatParams(params) { 107 | const formattedParams = {}; 108 | const successKeys = [ 109 | 'sdkVersionName', 110 | 'sdkVersionCode', 111 | 'osName', 112 | 'networkType', 113 | 'requestName', 114 | 'requestResult', 115 | 'bucket', 116 | 'region', 117 | 'appid', 118 | 'accelerate', 119 | 'url', 120 | 'host', 121 | 'requestPath', 122 | 'userAgent', 123 | 'httpMethod', 124 | 'httpSize', 125 | 'httpSpeed', 126 | 'httpTookTime', 127 | 'httpMd5', 128 | 'httpSign', 129 | 'httpFullTime', 130 | 'httpDomain', 131 | 'partNumber', 132 | 'httpRetryTimes', 133 | 'customId', 134 | 'traceId', 135 | 'realApi', 136 | ]; 137 | const failureKeys = [ 138 | ...successKeys, 139 | 'errorNode', 140 | 'errorCode', 141 | 'errorName', 142 | 'errorMessage', 143 | 'errorRequestId', 144 | 'errorHttpCode', 145 | 'errorServiceName', 146 | 'errorType', 147 | 'fullError', 148 | ]; 149 | // 需要上报的参数字段 150 | const reporterKeys = params.requestResult === 'Success' ? successKeys : failureKeys; 151 | for (let key in params) { 152 | if (!reporterKeys.includes(key)) continue; 153 | const formattedKey = camel2underline(key); 154 | formattedParams[formattedKey] = params[key]; 155 | } 156 | formattedParams['request_name'] = params.realApi ? transApiName(params.realApi) : params.requestName; 157 | return formattedParams; 158 | } 159 | 160 | // 链路追踪器 161 | class Tracker { 162 | constructor(opt) { 163 | const { 164 | parent, 165 | traceId, 166 | bucket, 167 | region, 168 | apiName, 169 | realApi, 170 | httpMethod, 171 | fileKey, 172 | fileSize, 173 | accelerate, 174 | customId, 175 | delay, 176 | deepTracker, 177 | Beacon, 178 | clsReporter, 179 | } = opt; 180 | const appid = (bucket && bucket.substr(bucket.lastIndexOf('-') + 1)) || ''; 181 | this.parent = parent; 182 | this.deepTracker = deepTracker; 183 | this.delay = delay; 184 | if (clsReporter && !this.clsReporter) { 185 | this.clsReporter = clsReporter; 186 | } 187 | // 上报用到的字段 188 | this.params = { 189 | // 通用字段 190 | sdkVersionName: 'cos-wx-sdk-v5', 191 | sdkVersionCode: pkg.version, 192 | osName: deviceInfo.devicePlatform, 193 | networkType: '', 194 | 195 | requestName: apiName || '', 196 | requestResult: '', // sdk api调用结果Success、Failure 197 | realApi, 198 | 199 | bucket, 200 | region, 201 | accelerate, 202 | httpMethod, 203 | url: '', 204 | host: '', 205 | httpDomain: '', 206 | requestPath: fileKey || '', 207 | 208 | errorType: '', 209 | errorCode: '', 210 | errorName: '', 211 | errorMessage: '', 212 | errorRequestId: '', 213 | errorHttpCode: 0, 214 | errorServiceName: '', 215 | errorNode: '', 216 | 217 | httpTookTime: 0, // http整体耗时 218 | httpSize: fileSize || 0, // 主要是文件大小,大小 B 219 | httpMd5: 0, // MD5耗时 220 | httpSign: 0, // 计算签名耗时 221 | httpFullTime: 0, // 任务整体耗时(包括md5、签名等) 222 | httpSpeed: 0, // 主要关注上传速度,KB/s 223 | 224 | size: fileSize || 0, 225 | httpMd5: 0, // MD5耗时 226 | httpSign: 0, // 计算签名耗时 227 | httpFull: 0, // http请求耗时 228 | name: apiName || '', 229 | tookTime: 0, // 总耗时 230 | md5StartTime: 0, // md5计算开始时间 231 | md5EndTime: 0, // md5计算结束时间 232 | signStartTime: 0, // 计算签名开始时间 233 | signEndTime: 0, // 计算签名结束时间 234 | httpStartTime: 0, // 发起网络请求开始时间 235 | httpEndTime: 0, // 网路请求结束时间 236 | startTime: new Date().getTime(), // sdk api调用起始时间,不是纯网络耗时 237 | endTime: 0, // sdk api调用结束时间,不是纯网络耗时 238 | 239 | // 小程序补充字段 240 | traceId: traceId || utils.getUid(), // 每条上报唯一标识 241 | appid, 242 | partNumber: 0, // 分块上传编号 243 | httpRetryTimes: 0, // sdk内部发起的请求重试 244 | customId: customId || '', // 业务id 245 | partTime: 0, 246 | }; 247 | if (Beacon) { 248 | this.beacon = getBeacon(Beacon, delay); 249 | } 250 | } 251 | 252 | // 格式化sdk回调 253 | async formatResult(err, data) { 254 | /** 255 | * 解析到err的格式为: 256 | * 1.服务端有返回时 257 | * { 258 | * err: 同下方error, 259 | * error: { 260 | * error: { 261 | * Code: '', Message: '', Resource: '', RequestId: '', TraceId: '', 262 | * }, 263 | * statusCode: xxx, 264 | * headers: {}, 265 | * RequestId: '', 266 | * }, 267 | * } 268 | * 2.本地抛出或小程序直接报错 269 | * {error: 'message'}或{error: {error: 'message' }} 270 | */ 271 | const now = new Date().getTime(); 272 | const networkType = await utils.getNetType(); 273 | const errorCode = err ? err?.error?.error?.Code || 'Error' : ''; 274 | const errorMessage = err ? err?.error?.error?.Message || err?.error?.error || err?.error || '' : ''; 275 | const errorName = errorMessage; 276 | const errorHttpCode = err ? err?.error?.statusCode : data.statusCode; 277 | const errorServiceName = err ? err?.error?.error?.Resource : ''; 278 | const requestId = err ? err?.error?.RequestId || '' : data?.RequestId || ''; 279 | const errorType = err ? (requestId ? 'Server' : 'Client') : ''; 280 | 281 | if (this.params.requestName === 'getObject') { 282 | this.params.httpSize = data ? data.headers && data.headers['content-length'] : 0; 283 | } 284 | 285 | // 上报 sliceUploadFile || uploadFile || uploadFiles 命中分块上传时 286 | const isSliceUploadFile = this.params.realApi === 'sliceUploadFile'; 287 | const isSliceCopyFile = this.params.realApi === 'sliceCopyFile'; 288 | 289 | if (isSliceUploadFile || isSliceCopyFile) { 290 | const speed = this.params.httpSize / 1024 / this.params.partTime; 291 | Object.assign(this.params, { httpSpeed: speed < 0 ? 0 : speed.toFixed(3) }); 292 | } else { 293 | const httpFullTime = now - this.params.startTime; 294 | const httpTookTime = this.params.httpEndTime - this.params.httpStartTime; 295 | const speed = this.params.httpSize / 1024 / (httpTookTime / 1000); 296 | const httpMd5 = this.params.md5EndTime - this.params.md5StartTime; 297 | const httpSign = this.params.signEndTime - this.params.signStartTime; 298 | 299 | if (this.parent) { 300 | this.parent.addParamValue('httpTookTime', ms2s(httpTookTime)); 301 | this.parent.addParamValue('httpFullTime', ms2s(httpFullTime)); 302 | this.parent.addParamValue('httpMd5', ms2s(httpMd5)); 303 | this.parent.addParamValue('httpSign', ms2s(httpSign)); 304 | if (['multipartUpload', 'uploadPartCopy', 'putObjectCopy'].includes(this.params.requestName)) { 305 | // 只有小分块上传|复制才累计纯请求耗时,计算速度时用到 306 | this.parent.addParamValue('partTime', ms2s(httpTookTime)); 307 | } 308 | } 309 | Object.assign(this.params, { 310 | httpFullTime: ms2s(httpFullTime), 311 | httpMd5: ms2s(httpMd5), 312 | httpSign: ms2s(httpSign), 313 | httpTookTime: ms2s(httpTookTime), 314 | httpSpeed: speed < 0 ? 0 : speed.toFixed(3), 315 | }); 316 | } 317 | 318 | Object.assign(this.params, { 319 | networkType, 320 | requestResult: err ? 'Failure' : 'Success', 321 | errorType, 322 | errorCode, 323 | errorHttpCode, 324 | errorName, 325 | errorMessage, 326 | errorServiceName, 327 | errorRequestId: requestId, 328 | }); 329 | if (err && (!errorCode || !errorMessage)) { 330 | // 暂存全量err一段时间 观察是否所有err格式都可被解析 331 | this.params.fullError = err ? JSON.stringify(err) : ''; 332 | } 333 | if (this.params.name === 'getObject') { 334 | this.params.size = data ? data.headers && data.headers['content-length'] : -1; 335 | } 336 | if (this.params.url) { 337 | try { 338 | const exec = /^http(s)?:\/\/(.*?)\//.exec(this.params.url); 339 | this.params.host = exec[2]; 340 | } catch (e) { 341 | this.params.host = this.params.url; 342 | } 343 | this.params.httpDomain = this.params.host; 344 | } 345 | } 346 | 347 | // 上报 348 | async report(err, data) { 349 | if (!this.beacon && !this.clsReporter) return; 350 | await this.formatResult(err, data); 351 | const formattedParams = formatParams(this.params); 352 | if (this.beacon) { 353 | this.sendEventsToBeacon(formattedParams); 354 | } 355 | if (this.clsReporter) { 356 | this.sendEventsToCLS(formattedParams); 357 | } 358 | } 359 | 360 | // 设置当前链路的参数 361 | setParams(params) { 362 | Object.assign(this.params, params); 363 | } 364 | 365 | addParamValue(key, value) { 366 | this.params[key] = (+this.params[key] + +value).toFixed(3); 367 | } 368 | 369 | // 上报灯塔 370 | sendEventsToBeacon(formattedParams) { 371 | // DeepTracker模式下才会上报分块上传内部细节 372 | const isSliceUploadFile = 373 | this.params.requestName === 'sliceUploadFile' || this.params.realApi === 'sliceUploadFile'; 374 | if (isSliceUploadFile && !this.deepTracker) { 375 | return; 376 | } 377 | const eventCode = 'qcloud_track_cos_sdk'; 378 | 379 | if (this.delay === 0) { 380 | // 实时上报 381 | this.beacon && this.beacon.onDirectUserAction(eventCode, formattedParams); 382 | } else { 383 | // 周期性上报 384 | this.beacon && this.beacon.onUserAction(eventCode, formattedParams); 385 | } 386 | } 387 | 388 | // 上报 cls 389 | sendEventsToCLS(formattedParams) { 390 | // 是否实时上报 391 | const immediate = !!(this.delay === 0); 392 | 393 | this.clsReporter.log(formattedParams, immediate); 394 | } 395 | 396 | // 生成子实例,与父所属一个链路,可用于分块上传内部流程上报单个分块操作 397 | generateSubTracker(subParams) { 398 | Object.assign(subParams, { 399 | parent: this, 400 | deepTracker: this.deepTracker, 401 | traceId: this.params.traceId, 402 | bucket: this.params.bucket, 403 | region: this.params.region, 404 | accelerate: this.params.accelerate, 405 | fileKey: this.params.requestPath, 406 | customId: this.params.customId, 407 | delay: this.params.delay, 408 | clsReporter: this.clsReporter, 409 | }); 410 | return new Tracker(subParams); 411 | } 412 | } 413 | 414 | module.exports = Tracker; 415 | -------------------------------------------------------------------------------- /demo/ciDemo/docPreview.js: -------------------------------------------------------------------------------- 1 | const COS = require('../lib/cos-wx-sdk-v5'); 2 | var wxfs = wx.getFileSystemManager(); 3 | const config = require('../config'); 4 | const { cos, requestCallback } = require('../tools'); 5 | 6 | const docPreviewDao = { 7 | '查询文档预览开通状态 describeDocProcessService': describeDocProcessService, 8 | '文档转html同步请求 getDocHtmlPreviewUrl': getDocHtmlPreviewUrl, 9 | '文档转码同步请求 getDocPreview': getDocPreview, 10 | '提交文档转码任务 createDocProcessJob': createDocProcessJob, 11 | '查询指定文档转码任务 describeDocProcessJob': describeDocProcessJob, 12 | '拉取符合条件的文档转码任务 describeDocProcessJobList': describeDocProcessJobList, 13 | '查询文档转码队列 describeDocProcessQueue': describeDocProcessQueue, 14 | '更新文档转码队列 updateProcessQueue': updateProcessQueue, 15 | }; 16 | 17 | function describeDocProcessService() { 18 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 19 | const key = `docbucket`; // 20 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 21 | const url = `https://${host}/${key}`; 22 | cos.request( 23 | { 24 | Method: 'GET', // 固定值,必须 25 | Key: key, // 必须 26 | Url: url, // 请求的url,必须 27 | Query: { 28 | // 地域信息,以“,”分隔字符串,支持 All、ap-shanghai、ap-beijing;是否必传:否 29 | regions: '', 30 | // 存储桶名称,以“,”分隔,支持多个存储桶,精确搜索;是否必传:否 31 | bucketNames: '', 32 | // 存储桶名称前缀,前缀搜索;是否必传:否 33 | bucketName: '', 34 | // 第几页;是否必传:否 35 | pageNumber: '', 36 | // 每页个数;是否必传:否 37 | pageSize: '', 38 | }, 39 | }, 40 | function (err, data) { 41 | if (err) { 42 | // 处理请求失败 43 | console.log(err); 44 | } else { 45 | // 处理请求成功 46 | console.log(data.Response); 47 | } 48 | } 49 | ); 50 | } 51 | 52 | function getDocHtmlPreviewUrl() { 53 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 54 | const ObjectKey = 'test.docx'; 55 | const key = `${ObjectKey}`; // ObjectKey:{ObjectKey}; 56 | const host = `${config.Bucket}.cos.${config.Region}.myqcloud.com`; 57 | const url = `https://${host}/${key}`; 58 | 59 | cos.request( 60 | { 61 | Method: 'GET', // 固定值,必须 62 | Key: key, // 必须 63 | Url: url, // 请求的url,必须 64 | Query: { 65 | // 数据万象处理能力,文档 HTML 预览固定为 doc-preview;是否必传:是 66 | 'ci-process': 'doc-preview', 67 | // 转换输出目标文件类型,文档 HTML 预览固定为 html(需为小写字母);是否必传:是 68 | dstType: 'html', 69 | // 是否获取预览链接。填入值为1会返回预览链接和Token信息;填入值为2只返回Token信息;不传会直接预览;是否必传:否 70 | weboffice_url: 1, 71 | // 指定目标文件类型,支持的文件类型请见下方;是否必传:否 72 | srcType: '', 73 | // 对象下载签名,如果预览的对象为私有读时,需要传入签名,详情请参见 请求签名 文档注意:需要进行 urlencode;是否必传:否 74 | sign: '', 75 | // 是否可复制。默认为可复制,填入值为1;不可复制,填入值为0;是否必传:否 76 | copyable: '', 77 | // 自定义配置参数,json结构,需要经过 URL 安全 的 Base64 编码,默认配置为:{ commonOptions: { isShowTopArea: true, isShowHeader: true, language: "zh" }},支持的配置参考 自定义配置项说明。htmlParams支持的特殊配置:语言切换,通过 commonOptions 的 language 参数指定预览语言,支持"zh"、"en“,默认为"zh"。;是否必传:否 78 | htmlParams: '', 79 | // 水印文字,需要经过 URL 安全 的 Base64 编码,默认为空;是否必传:否 80 | htmlwaterword: '', 81 | // 水印 RGBA(颜色和透明度),需要经过 URL 安全 的 Base64 编码,默认为:rgba(192,192,192,0.6);是否必传:否 82 | htmlfillstyle: '', 83 | // 水印文字样式,需要经过 URL 安全 的 Base64 编码,默认为:bold 20px Serif;是否必传:否 84 | htmlfront: '', 85 | // 水印文字旋转角度,0 - 360,默认315度;是否必传:否 86 | htmlrotate: '', 87 | // 水印文字水平间距,单位 px,默认为50;是否必传:否 88 | htmlhorizontal: '', 89 | // 水印文字垂直间距,单位 px,默认为100;是否必传:否 90 | htmlvertical: '', 91 | }, 92 | }, 93 | function (err, data) { 94 | if (err) { 95 | // 处理请求失败 96 | console.log(err); 97 | } else { 98 | // 处理请求成功 99 | console.log(data.Response); 100 | } 101 | } 102 | ); 103 | } 104 | 105 | function getDocPreview() { 106 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 107 | const ObjectKey = 'test.docx'; 108 | const key = `${ObjectKey}`; // ObjectKey:{ObjectKey}; 109 | const host = `${config.Bucket}.cos.${config.Region}.myqcloud.com`; 110 | const url = `https://${host}/${key}`; 111 | 112 | cos.request( 113 | { 114 | Method: 'GET', // 固定值,必须 115 | Key: key, // 必须 116 | Url: url, // 请求的url,必须 117 | Query: { 118 | // 数据万象处理能力,文档预览固定为 doc-preview;是否必传:是 119 | 'ci-process': 'doc-preview', 120 | // 源数据的后缀类型,当前文档转换根据 COS 对象的后缀名来确定源数据类型。当 COS 对象没有后缀名时,可以设置该值;是否必传:否 121 | srcType: '', 122 | // 需转换的文档页码,默认从1开始计数;表格文件中 page 表示转换的第 X 个 sheet 的第 X 张图;是否必传:否 123 | page: 0, 124 | // 转换输出目标文件类型:png,转成 png 格式的图片文件jpg,转成 jpg 格式的图片文件pdf,转成 pdf 格式文件。 无法选择页码,page 参数不生效如果传入的格式未能识别,默认使用 jpg 格式;是否必传:否 125 | dstType: 'jpg', 126 | // Office 文档的打开密码,如果需要转换有密码的文档,请设置该字段;是否必传:否 127 | password: '', 128 | // 是否隐藏批注和应用修订,默认为00:隐藏批注,应用修订1:显示批注和修订;是否必传:否 129 | comment: 0, 130 | // 表格文件参数,转换第 X 个表,默认为1;是否必传:否 131 | sheet: 0, 132 | // 表格文件转换纸张方向,0代表垂直方向,非0代表水平方向,默认为0;是否必传:否 133 | excelPaperDirection: 0, 134 | // 设置纸张(画布)大小,对应信息为: 0 → A4 、 1 → A2 、 2 → A0 ,默认 A4 纸张 (需配合 excelRow 或 excelCol 一起使用);是否必传:否 135 | excelPaperSize: 0, 136 | // 转换后的图片处理参数,支持 基础图片处理 所有处理参数,多个处理参数可通过 管道操作符 分隔,从而实现在一次访问中按顺序对图片进行不同处理;是否必传:否 137 | ImageParams: '', 138 | // 生成预览图的图片质量,取值范围为 [1, 100],默认值100。 例如取值为100,代表生成图片质量为100%;是否必传:否 139 | quality: 0, 140 | // 预览图片的缩放参数,取值范围为 [10, 200], 默认值100。 例如取值为200,代表图片缩放比例为200% 即放大两倍;是否必传:否 141 | scale: 0, 142 | // 按指定 dpi 渲染图片,该参数与 scale 共同作用,取值范围 96-600 ,默认值为 96 。转码后的图片单边宽度需小于65500像素;是否必传:否 143 | imageDpi: 0, 144 | }, 145 | }, 146 | function (err, data) { 147 | if (err) { 148 | // 处理请求失败 149 | console.log(err); 150 | } else { 151 | // 处理请求成功 152 | console.log(data.Response); 153 | } 154 | } 155 | ); 156 | } 157 | 158 | function createDocProcessJob() { 159 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 160 | const key = `doc_jobs`; // 161 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 162 | const url = `https://${host}/${key}`; 163 | const body = COS.util.json2xml({ 164 | Request: { 165 | // 创建任务的 Tag,目前仅支持:DocProcess;是否必传:是 166 | Tag: 'DocProcess', 167 | // 待操作的文件对象;是否必传:是 168 | Input: { 169 | // 文件在 COS 上的文件路径,Bucket 由 Host 指定;是否必传:是 170 | Object: 'test.docx', 171 | }, 172 | // 操作规则;是否必传:是 173 | Operation: { 174 | // 当 Tag 为 DocProcess 时有效,指定该任务的参数;是否必传:否 175 | DocProcess: { 176 | // 源数据的后缀类型,当前文档转换根据 cos 对象的后缀名来确定源数据类型,当 cos 对象没有后缀名时,可以设置该值;是否必传:否 177 | SrcType: '', 178 | // 转换输出目标文件类型:jpg,转成 jpg 格式的图片文件;如果传入的格式未能识别,默认使用 jpg 格式png,转成 png 格式的图片文件pdf,转成 pdf 格式文件(暂不支持指定页数);是否必传:否 179 | TgtType: '', 180 | // 从第 X 页开始转换;在表格文件中,一张表可能分割为多页转换,生成多张图片。StartPage 表示从指定 SheetId 的第 X 页开始转换。默认为1;是否必传:否 181 | StartPage: 1, 182 | // 转换至第 X 页;在表格文件中,一张表可能分割为多页转换,生成多张图片。EndPage 表示转换至指定 SheetId 的第 X 页。默认为-1,即转换全部页;是否必传:否 183 | EndPage: 2, 184 | // 表格文件参数,转换第 X 个表,默认为0;设置 SheetId 为0,即转换文档中全部表;是否必传:否 185 | SheetId: 0, 186 | // 表格文件转换纸张方向,0代表垂直方向,非0代表水平方向,默认为0;是否必传:否 187 | PaperDirection: 0, 188 | // 设置纸张(画布)大小,对应信息为: 0 → A4 、 1 → A2 、 2 → A0 ,默认 A4 纸张;是否必传:否 189 | PaperSize: 0, 190 | // 转换后的图片处理参数,支持 基础图片处理 所有处理参数,多个处理参数可通过 管道操作符 分隔,从而实现在一次访问中按顺序对图片进行不同处理;是否必传:否 191 | ImageParams: '', 192 | // 生成预览图的图片质量,取值范围 [1-100],默认值100。 例:值为100,代表生成图片质量为100%;是否必传:否 193 | Quality: 0, 194 | // 预览图片的缩放参数,取值范围[10-200], 默认值100。 例:值为200,代表图片缩放比例为200% 即放大两倍;是否必传:否 195 | Zoom: 100, 196 | // 按指定 dpi 渲染图片,该参数与 Zoom 共同作用,取值范围 96-600 ,默认值为 96 。转码后的图片单边宽度需小于65500像素;是否必传:否 197 | ImageDpi: 96, 198 | // 是否转换成单张长图,设置为 1 时,最多仅支持将 20 标准页面合成单张长图,超过可能会报错,分页范围可以通过 StartPage、EndPage 控制。默认值为 0 ,按页导出图片,TgtType="png"/"jpg" 时生效;是否必传:否 199 | PicPagination: 0, 200 | }, 201 | // 结果输出地址;是否必传:是 202 | Output: { 203 | // 存储桶的地域;是否必传:是 204 | Region: config.Region, 205 | // 存储结果的存储桶;是否必传:是 206 | Bucket: config.Bucket, 207 | // 输出文件路径。非表格文件输出文件名需包含 ${Number} 或 ${Page} 参数。多个输出文件,${Number} 表示序号从1开始,${Page} 表示序号与预览页码一致。${Number} 表示多个输出文件,序号从1开始,例如输入 abc_${Number}.jpg,预览某文件5 - 6页,则输出文件名为 abc_1.jpg,abc_2.jpg${Page} 表示多个输出文件,序号与预览页码一致,例如输入 abc_${Page}.jpg,预览某文件5-6页,则输出文件名为 abc_5.jpg,abc_6.jpg表格文件输出路径需包含 ${SheetID} 占位符,输出文件名必须包含 ${Number} 参数。例如 /${SheetID}/abc_${Number}.jpg,先根据 excel 转换的表格数,生成对应数量的文件夹,再在对应的文件夹下,生成对应数量的图片文件;是否必传:是 208 | Object: '${Number}', 209 | }, 210 | }, 211 | // 任务所在的队列 ID,开通预览服务后自动生成,请使用 查询队列 获取或前往 万象控制台 在存储桶中查询 ;是否必传:否 212 | QueueId: '', 213 | }, 214 | }); 215 | cos.request( 216 | { 217 | Method: 'POST', // 固定值,必须 218 | Key: key, // 必须 219 | Url: url, // 请求的url,必须 220 | Body: body, // 请求体参数,必须 221 | ContentType: 'application/xml', // 固定值,必须 222 | }, 223 | function (err, data) { 224 | if (err) { 225 | // 处理请求失败 226 | console.log(err); 227 | } else { 228 | // 处理请求成功 229 | console.log(data.Response); 230 | } 231 | } 232 | ); 233 | } 234 | 235 | function describeDocProcessJob() { 236 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 237 | const jobId = 'xxx'; 238 | const key = `doc_jobs/${jobId}`; // jobId:{jobId}; 239 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 240 | const url = `https://${host}/${key}`; 241 | 242 | cos.request( 243 | { 244 | Method: 'GET', // 固定值,必须 245 | Key: key, // 必须 246 | Url: url, // 请求的url,必须 247 | }, 248 | function (err, data) { 249 | if (err) { 250 | // 处理请求失败 251 | console.log(err); 252 | } else { 253 | // 处理请求成功 254 | console.log(data.Response); 255 | } 256 | } 257 | ); 258 | } 259 | 260 | function describeDocProcessJobList() { 261 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 262 | const key = `doc_jobs`; // 263 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 264 | const url = `https://${host}/${key}`; 265 | cos.request( 266 | { 267 | Method: 'GET', // 固定值,必须 268 | Key: key, // 必须 269 | Url: url, // 请求的url,必须 270 | Query: { 271 | // 任务的 Tag:DocProcess;是否必传:是 272 | tag: 'DocProcess', 273 | // 拉取该队列 ID 下的任务,可在任务响应内容或控制台中获取;是否必传:否 274 | queueId: '', 275 | // Desc 或者 Asc。默认为 Desc;是否必传:否 276 | orderByTime: 'Desc', 277 | // 请求的上下文,用于翻页。上次返回的值;是否必传:否 278 | nextToken: '', 279 | // 拉取该状态的任务,以,分割,支持多状态:All、Submitted、Running、Success、Failed、Pause、Cancel。默认为 All;是否必传:否 280 | states: 'Success', 281 | // 拉取创建时间大于等于该时间的任务。格式为:%Y-%m-%dT%H:%m:%S%z;是否必传:否 282 | // startCreationTime: "", 283 | // 拉取创建时间小于等于该时间的任务。格式为:%Y-%m-%dT%H:%m:%S%z;是否必传:否 284 | // endCreationTime: "", 285 | }, 286 | }, 287 | function (err, data) { 288 | if (err) { 289 | // 处理请求失败 290 | console.log(err); 291 | } else { 292 | // 处理请求成功 293 | console.log(data.Response); 294 | } 295 | } 296 | ); 297 | } 298 | 299 | function describeDocProcessQueue() { 300 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 301 | const key = `docqueue`; // 302 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 303 | const url = `https://${host}/${key}`; 304 | cos.request( 305 | { 306 | Method: 'GET', // 固定值,必须 307 | Key: key, // 必须 308 | Url: url, // 请求的url,必须 309 | Query: { 310 | // 队列 ID,以“,”符号分割字符串;是否必传:否 311 | queueIds: '', 312 | // 1. Active 表示队列内的作业会被文档预览服务调度执行2. Paused 表示队列暂停,作业不再会被文档预览服务调度执行,队列内的所有作业状态维持在暂停状态,已经处于执行中的任务将继续执行,不受影响;是否必传:否 313 | state: 'Active', 314 | // 第几页,默认第一页;是否必传:否 315 | pageNumber: '', 316 | // 每页个数,默认10个;是否必传:否 317 | pageSize: '', 318 | }, 319 | }, 320 | function (err, data) { 321 | if (err) { 322 | // 处理请求失败 323 | console.log(err); 324 | } else { 325 | // 处理请求成功 326 | console.log(data.Response); 327 | } 328 | } 329 | ); 330 | } 331 | 332 | function updateProcessQueue() { 333 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 334 | const queueId = 'xxx'; 335 | const key = `docqueue/${queueId}`; 336 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 337 | const url = `https://${host}/${key}`; 338 | const body = COS.util.json2xml({ 339 | Request: { 340 | // 队列名称;是否必传:是 341 | Name: 'xxx', 342 | // 队列 ID;是否必传:是 343 | QueueID: queueId, 344 | // 队列状态;是否必传:是 345 | State: 'Active', 346 | // 通知渠道;是否必传:是 347 | NotifyConfig: { 348 | // 回调配置;是否必传:否 349 | Url: '', 350 | // 回调类型,普通回调:Url;是否必传:否 351 | Type: 'Url', 352 | // 回调事件,文档预览任务完成;是否必传:否 353 | Event: 'TaskFinish', 354 | // 回调开关,Off,On;是否必传:否 355 | State: 'Off', 356 | }, 357 | }, 358 | }); 359 | cos.request( 360 | { 361 | Method: 'PUT', // 固定值,必须 362 | Key: key, // 必须 363 | Url: url, // 请求的url,必须 364 | Body: body, // 请求体参数,必须 365 | ContentType: 'application/xml', // 固定值,必须 366 | }, 367 | function (err, data) { 368 | if (err) { 369 | // 处理请求失败 370 | console.log(err); 371 | } else { 372 | // 处理请求成功 373 | console.log(data.Response); 374 | } 375 | } 376 | ); 377 | } 378 | 379 | module.exports = docPreviewDao; 380 | -------------------------------------------------------------------------------- /demo/ciDemo/fileProcess.js: -------------------------------------------------------------------------------- 1 | const COS = require('../lib/cos-wx-sdk-v5'); 2 | var wxfs = wx.getFileSystemManager(); 3 | const config = require('../config'); 4 | const { cos, requestCallback } = require('../tools'); 5 | 6 | const fileProcessDao = { 7 | '开通文件处理服务 openFileProcessService': openFileProcessService, 8 | '查询文件处理服务 describeFileProcessService': describeFileProcessService, 9 | '关闭文件处理服务 closeFileProcessService': closeFileProcessService, 10 | '查询文件处理队列 describeFileProcessQueue': describeFileProcessQueue, 11 | '更新文件处理队列 updateFileProcessQueue': updateFileProcessQueue, 12 | '哈希值计算同步请求 fileHash': fileHash, 13 | '提交哈希值计算任务 createFileHashJob': createFileHashJob, 14 | '查询哈希值计算结果 describeFileHashJob': describeFileHashJob, 15 | '提交多文件打包压缩任务 createFileCompressJob': createFileCompressJob, 16 | '查询多文件打包压缩结果 describeFileCompressJob': describeFileCompressJob, 17 | '压缩包预览 zipPreview': zipPreview, 18 | '提交文件解压任务 createFileUnCompressJob': createFileUnCompressJob, 19 | '查询文件解压结果 describeFileUnCompressJob': describeFileUnCompressJob, 20 | }; 21 | 22 | function openFileProcessService() { 23 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 24 | const key = `file_bucket`; // 25 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 26 | const url = `https://${host}/${key}`; 27 | 28 | cos.request( 29 | { 30 | Method: 'POST', // 固定值,必须 31 | Key: key, // 必须 32 | Url: url, // 请求的url,必须 33 | }, 34 | function (err, data) { 35 | if (err) { 36 | // 处理请求失败 37 | console.log(err); 38 | } else { 39 | // 处理请求成功 40 | console.log(data.Response); 41 | } 42 | } 43 | ); 44 | } 45 | 46 | function describeFileProcessService() { 47 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 48 | const key = `file_bucket`; // 49 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 50 | const url = `https://${host}/${key}`; 51 | 52 | cos.request( 53 | { 54 | Method: 'Get', // 固定值,必须 55 | Key: key, // 必须 56 | Url: url, // 请求的url,必须 57 | Query: { 58 | // regions: "", // 地域信息,例如 ap-shanghai、ap-beijing,若查询多个地域以“,”分隔字符串,非必须 59 | // bucketNames: "", // 存储桶名称,以“,”分隔,支持多个存储桶,精确搜索,非必须 60 | // bucketName: "", // 存储桶名称前缀,前缀搜索,非必须 61 | // pageNumber: "", // 第几页,非必须 62 | // pageSize: "", // 每页个数,大于0且小于等于100的整数,非必须 63 | }, 64 | }, 65 | function (err, data) { 66 | if (err) { 67 | // 处理请求失败 68 | console.log(err); 69 | } else { 70 | // 处理请求成功 71 | console.log(data.Response); 72 | } 73 | } 74 | ); 75 | } 76 | 77 | function closeFileProcessService() { 78 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 79 | const key = `file_bucket`; // 80 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 81 | const url = `https://${host}/${key}`; 82 | 83 | cos.request( 84 | { 85 | Method: 'DELETE', // 固定值,必须 86 | Key: key, // 必须 87 | Url: url, // 请求的url,必须 88 | }, 89 | function (err, data) { 90 | if (err) { 91 | // 处理请求失败 92 | console.log(err); 93 | } else { 94 | // 处理请求成功 95 | console.log(data.Response); 96 | } 97 | } 98 | ); 99 | } 100 | 101 | function describeFileProcessQueue() { 102 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 103 | const key = `file_queue`; // 104 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 105 | const url = `https://${host}/${key}`; 106 | 107 | cos.request( 108 | { 109 | Method: 'GET', // 固定值,必须 110 | Key: key, // 必须 111 | Url: url, // 请求的url,必须 112 | Query: { 113 | // 队列 ID,以“,”符号分割字符串;是否必传:否 114 | queueIds: '', 115 | // Active 表示队列内的作业会被调度执行Paused 表示队列暂停,作业不再会被调度执行,队列内的所有作业状态维持在暂停状态,已经执行中的任务不受影响;是否必传:否 116 | state: 'Active', 117 | // 第几页,默认值1;是否必传:否 118 | pageNumber: '', 119 | // 每页个数,默认值10;是否必传:否 120 | pageSize: '', 121 | }, 122 | }, 123 | function (err, data) { 124 | if (err) { 125 | // 处理请求失败 126 | console.log(err); 127 | } else { 128 | // 处理请求成功 129 | console.log(data.Response); 130 | } 131 | } 132 | ); 133 | } 134 | 135 | function updateFileProcessQueue() { 136 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 137 | const queueId = 'xxx'; 138 | const key = `file_queue/${queueId}`; // queueId:{queueId}; 139 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 140 | const url = `https://${host}/${key}`; 141 | const body = COS.util.json2xml({ 142 | Request: { 143 | // 队列名称,仅支持中文、英文、数字、_、-和*,长度不超过 128;是否必传:是 144 | Name: 'xxx', 145 | // Active 表示队列内的作业会被调度执行Paused 表示队列暂停,作业不再会被调度执行,队列内的所有作业状态维持在暂停状态,已经执行中的任务不受影响;是否必传:是 146 | State: 'Active', 147 | // 回调配置;是否必传:是 148 | NotifyConfig: { 149 | // 回调开关OffOn;是否必传:否 150 | State: 'Off', 151 | // 回调事件TaskFinish:任务完成WorkflowFinish:工作流完成;是否必传:否 152 | Event: 'TaskFinish', 153 | // 回调格式XMLJSON;是否必传:否 154 | ResultFormat: '', 155 | // 回调类型UrlTDMQ;是否必传:否 156 | Type: 'Url', 157 | // 回调地址,不能为内网地址。;是否必传:否 158 | Url: '', 159 | // TDMQ 使用模式Topic:主题订阅Queue: 队列服务;是否必传:否 160 | MqMode: '', 161 | // TDMQ 所属园区,目前支持园区 sh(上海)、bj(北京)、gz(广州)、cd(成都)、hk(中国香港);是否必传:否 162 | MqRegion: '', 163 | // TDMQ 主题名称;是否必传:否 164 | MqName: '', 165 | }, 166 | }, 167 | }); 168 | cos.request( 169 | { 170 | Method: 'PUT', // 固定值,必须 171 | Key: key, // 必须 172 | Url: url, // 请求的url,必须 173 | Body: body, // 请求体参数,必须 174 | ContentType: 'application/xml', // 固定值,必须 175 | }, 176 | function (err, data) { 177 | if (err) { 178 | // 处理请求失败 179 | console.log(err); 180 | } else { 181 | // 处理请求成功 182 | console.log(data.Response); 183 | } 184 | } 185 | ); 186 | } 187 | 188 | function fileHash() { 189 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 190 | const ObjectKey = 'test.docx'; 191 | const key = `${ObjectKey}`; // ObjectKey:{ObjectKey}; 192 | const host = `${config.Bucket}.cos.${config.Region}.myqcloud.com`; 193 | const url = `https://${host}/${key}`; 194 | 195 | cos.request( 196 | { 197 | Method: 'GET', // 固定值,必须 198 | Key: key, // 必须 199 | Url: url, // 请求的url,必须 200 | Query: { 201 | // 操作类型,哈希值计算固定为:filehash;是否必传:是 202 | 'ci-process': 'filehash', 203 | // 支持的哈希算法类型,有效值:md5、sha1、sha256;是否必传:是 204 | type: 'md5', 205 | // 是否将计算得到的哈希值,自动添加至文件的自定义header,格式为:x-cos-meta-md5/sha1/sha256;有效值: true、false,不填则默认为false;是否必传:否 206 | addtoheader: '', 207 | }, 208 | }, 209 | function (err, data) { 210 | if (err) { 211 | // 处理请求失败 212 | console.log(err); 213 | } else { 214 | // 处理请求成功 215 | console.log(data.Response); 216 | } 217 | } 218 | ); 219 | } 220 | 221 | function createFileHashJob() { 222 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 223 | const key = `jobs`; // 224 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 225 | const url = `https://${host}/${key}`; 226 | const body = COS.util.json2xml({ 227 | Request: { 228 | // 表示任务的类型,哈希值计算默认为:FileHashCode。;是否必传:是 229 | Tag: 'FileHashCode', 230 | // 包含待操作的文件信息。;是否必传:是 231 | Input: { 232 | // 文件名,取值为文件在当前存储桶中的完整名称。;是否必传:是 233 | Object: 'test.docx', 234 | }, 235 | // 包含哈希值计算的处理规则。;是否必传:是 236 | Operation: { 237 | // 指定哈希值计算的处理规则。;是否必传:是 238 | FileHashCodeConfig: { 239 | // 哈希值的算法类型,支持:MD5、SHA1、SHA256;是否必传:是 240 | Type: 'MD5', 241 | // 是否将计算得到的哈希值添加至文件自定义header,有效值:true、false,默认值为 false。自定义header根据Type的值变化,例如Type值为MD5时,自定义header为 x-cos-meta-md5。;是否必传:否 242 | AddToHeader: '', 243 | }, 244 | // 透传用户信息, 可打印的 ASCII 码,长度不超过1024。;是否必传:否 245 | UserData: '', 246 | }, 247 | // 任务回调格式,JSON 或 XML,默认 XML,优先级高于队列的回调格式。;是否必传:否 248 | CallBackFormat: '', 249 | // 任务回调类型,Url 或 TDMQ,默认 Url,优先级高于队列的回调类型。;是否必传:否 250 | CallBackType: 'Url', 251 | // 任务回调的地址,优先级高于队列的回调地址。;是否必传:否 252 | CallBack: '', 253 | }, 254 | }); 255 | cos.request( 256 | { 257 | Method: 'POST', // 固定值,必须 258 | Key: key, // 必须 259 | Url: url, // 请求的url,必须 260 | Body: body, // 请求体参数,必须 261 | ContentType: 'application/xml', // 固定值,必须 262 | }, 263 | function (err, data) { 264 | if (err) { 265 | // 处理请求失败 266 | console.log(err); 267 | } else { 268 | // 处理请求成功 269 | console.log(data.Response); 270 | } 271 | } 272 | ); 273 | } 274 | 275 | function describeFileHashJob() { 276 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 277 | const jobId = 'xxx'; 278 | const key = `file_jobs/${jobId}`; // jobId:{jobId}; 279 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 280 | const url = `https://${host}/${key}`; 281 | cos.request( 282 | { 283 | Method: 'GET', // 固定值,必须 284 | Key: key, // 必须 285 | Url: url, // 请求的url,必须 286 | }, 287 | function (err, data) { 288 | if (err) { 289 | // 处理请求失败 290 | console.log(err); 291 | } else { 292 | // 处理请求成功 293 | console.log(data.Response); 294 | } 295 | } 296 | ); 297 | } 298 | 299 | function createFileCompressJob() { 300 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 301 | const key = `jobs`; // 302 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 303 | const url = `https://${host}/${key}`; 304 | const body = COS.util.json2xml({ 305 | Request: { 306 | // 表示任务的类型,多文件打包压缩默认为:FileCompress。;是否必传:是 307 | Tag: 'FileCompress', 308 | // 包含文件打包压缩的处理规则。;是否必传:是 309 | Operation: { 310 | // 指定文件打包压缩的处理规则。;是否必传:是 311 | FileCompressConfig: { 312 | // 文件打包时,是否需要去除源文件已有的目录结构,有效值:0:不需要去除目录结构,打包后压缩包中的文件会保留原有的目录结构;1:需要,打包后压缩包内的文件会去除原有的目录结构,所有文件都在同一层级。例如:源文�� URL 为 https://domain/source/test.mp4,则源文件路径为 source/test.mp4,如果为 1,则 ZIP 包中该文件路径为 test.mp4;如果为0, ZIP 包中该文件路径为 source/test.mp4。;是否必传:是 313 | Flatten: '0', 314 | // 打包压缩的类型,有效值:zip、tar、tar.gz。;是否必传:是 315 | Format: 'zip', 316 | // 压缩类型,仅在Format为tar.gz或zip时有效。faster:压缩速度较快better:压缩质量较高,体积较小default:适中的压缩方式默认值为default;是否必传:否 317 | Type: '', 318 | // 压缩包密钥,传入时需先经过 base64 编码,编码后长度不能超过128。当 Format 为 zip 时生效。;是否必传:否 319 | CompressKey: '', 320 | // 支持将需要打包的文件整理成索引文件,后台将根据索引文件内提供的文件 url,打包为一个压缩包文件。索引文件需要保存在当前存储桶中,本字段需要提供索引文件的对象地址,例如:/test/index.csv。索引文件格式:仅支持 CSV 文件,一行一条 URL(仅支持本存储桶文件),如有多列字段,默认取第一列作为URL。最多不超过10000个文件, 总大小不超过50G,否则会导致任务失败。;是否必传:否 321 | UrlList: '', 322 | // 支持对存储桶中的某个前缀进行打包,如果需要对某个目录进行打包,需要加/,例如test目录打包,则值为:test/。最多不超过10000个文件,总大小不超过50G,否则会导致任务失败。;是否必传:否 323 | Prefix: 'example/', 324 | // 支持对存储桶中的多个文件进行打包,个数不能超过 1000,总大小不超过50G,否则会导致任务失败。;是否必传:否 325 | Key: '', 326 | }, 327 | // 透传用户信息,可打印的 ASCII 码,长度不超过1024。;是否必传:否 328 | UserData: '', 329 | // 指定打包压缩后的文件保存的地址信息。;是否必传:是 330 | Output: { 331 | // 存储桶的地域。;是否必传:是 332 | Region: config.Region, 333 | // 保存压缩后文件的存储桶。;是否必传:是 334 | Bucket: config.Bucket, 335 | // 压缩后文件的文件名;是否必传:是 336 | Object: 'test.zip', 337 | }, 338 | }, 339 | // 任务回调格式,JSON 或 XML,默认 XML,优先级高于队列的回调格式。;是否必传:否 340 | CallBackFormat: '', 341 | // 任务回调类型,Url 或 TDMQ,默认 Url,优先级高于队列的回调类型。;是否必传:否 342 | CallBackType: 'Url', 343 | // 任务回调的地址,优先级高于队列的回调地址。;是否必传:否 344 | CallBack: '', 345 | }, 346 | }); 347 | 348 | cos.request( 349 | { 350 | Method: 'POST', // 固定值,必须 351 | Key: key, // 必须 352 | Url: url, // 请求的url,必须 353 | Body: body, // 请求体参数,必须 354 | ContentType: 'application/xml', // 固定值,必须 355 | }, 356 | function (err, data) { 357 | if (err) { 358 | // 处理请求失败 359 | console.log(err); 360 | } else { 361 | // 处理请求成功 362 | console.log(data.Response); 363 | } 364 | } 365 | ); 366 | } 367 | 368 | function describeFileCompressJob() { 369 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 370 | const jobId = 'xxx'; 371 | const key = `file_jobs/${jobId}`; // jobId:{jobId}; 372 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 373 | const url = `https://${host}/${key}`; 374 | cos.request( 375 | { 376 | Method: 'GET', // 固定值,必须 377 | Key: key, // 必须 378 | Url: url, // 请求的url,必须 379 | }, 380 | function (err, data) { 381 | if (err) { 382 | // 处理请求失败 383 | console.log(err); 384 | } else { 385 | // 处理请求成功 386 | console.log(data.Response); 387 | } 388 | } 389 | ); 390 | } 391 | 392 | function zipPreview() { 393 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 394 | const key = `test.zip`; // 395 | const host = `${config.Bucket}.cos.${config.Region}.myqcloud.com`; 396 | const url = `https://${host}/${key}`; 397 | 398 | cos.request( 399 | { 400 | Method: 'GET', // 固定值,必须 401 | Key: key, // 必须 402 | Url: url, // 请求的url,必须 403 | Query: { 404 | 'ci-process': 'zippreview', // 操作类型,压缩包预览计算固定为:zippreview,必须 405 | }, 406 | }, 407 | function (err, data) { 408 | if (err) { 409 | // 处理请求失败 410 | console.log(err); 411 | } else { 412 | // 处理请求成功 413 | console.log(data.Response); 414 | } 415 | } 416 | ); 417 | } 418 | 419 | function createFileUnCompressJob() { 420 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 421 | const key = `jobs`; // 422 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 423 | const url = `https://${host}/${key}`; 424 | const body = COS.util.json2xml({ 425 | Request: { 426 | // 表示任务的类型,文件解压默认为:FileUncompress。;是否必传:是 427 | Tag: 'FileUncompress', 428 | // 包含待操作的文件信息。;是否必传:是 429 | Input: { 430 | // 文件名,取值为文件在当前存储桶中的完整名称。;是否必传:是 431 | Object: 'test.zip', 432 | }, 433 | // 包含文件解压的处理规则。;是否必传:是 434 | Operation: { 435 | // 指定文件解压的处理规则。;是否必传:是 436 | FileUncompressConfig: { 437 | // 指定解压后输出文件的前缀,不填则默认保存在存储桶根路径。;是否必传:否 438 | Prefix: 'output/', 439 | // 解压密钥,传入时需先经过 base64 编码。;是否必传:否 440 | UnCompressKey: '', 441 | // 指定解压后的文件路径是否需要替换前缀,有效值:0:不添加额外的前缀,解压缩将保存在Prefix指定的路径下(不会保留压缩包的名称,仅将压缩包内的文件保存至指定的路径)1:以压缩包本身的名称作为前缀,解压缩将保存在Prefix指定的路径下2:以压缩包完整路径作为前缀,此时如果不指定Prefix,就是解压到压缩包所在的当前路径(包含压缩包本身名称)默认值为0。;是否必传:否 442 | PrefixReplaced: '', 443 | }, 444 | // 透传用户信息,可打印的 ASCII 码,长度不超过1024。;是否必传:否 445 | UserData: '', 446 | // 指定解压后的文件保存的存储桶信息。;是否必传:是 447 | Output: { 448 | // 存储桶的地域。;是否必传:是 449 | Region: config.Region, 450 | // 保存解压后文件的存储桶。;是否必传:是 451 | Bucket: config.Bucket, 452 | }, 453 | }, 454 | // 任务回调格式,JSON 或 XML,默认 XML,优先级高于队列的回调格式。;是否必传:否 455 | CallBackFormat: '', 456 | // 任务回调类型,Url 或 TDMQ,默认 Url,优先级高于队列的回调类型。;是否必传:否 457 | CallBackType: 'Url', 458 | // 任务回调的地址,优先级高于队列的回调地址。;是否必传:否 459 | CallBack: '', 460 | }, 461 | }); 462 | cos.request( 463 | { 464 | Method: 'POST', // 固定值,必须 465 | Key: key, // 必须 466 | Url: url, // 请求的url,必须 467 | Body: body, // 请求体参数,必须 468 | ContentType: 'application/xml', // 固定值,必须 469 | }, 470 | function (err, data) { 471 | if (err) { 472 | // 处理请求失败 473 | console.log(err); 474 | } else { 475 | // 处理请求成功 476 | console.log(data.Response); 477 | } 478 | } 479 | ); 480 | } 481 | 482 | function describeFileUnCompressJob() { 483 | // sdk引入以及初始化请参考:https://cloud.tencent.com/document/product/436/31953 484 | const jobId = 'xxx'; 485 | const key = `file_jobs/${jobId}`; // jobId:{jobId}; 486 | const host = `${config.Bucket}.ci.${config.Region}.myqcloud.com`; 487 | const url = `https://${host}/${key}`; 488 | cos.request( 489 | { 490 | Method: 'GET', // 固定值,必须 491 | Key: key, // 必须 492 | Url: url, // 请求的url,必须 493 | }, 494 | function (err, data) { 495 | if (err) { 496 | // 处理请求失败 497 | console.log(err); 498 | } else { 499 | // 处理请求成功 500 | console.log(data.Response); 501 | } 502 | } 503 | ); 504 | } 505 | 506 | module.exports = fileProcessDao; 507 | -------------------------------------------------------------------------------- /demo/lib/beacon_mp.min.js: -------------------------------------------------------------------------------- 1 | /*! For license information please see beacon_mp.min.js.LICENSE.txt */ 2 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.BeaconAction=e():t.BeaconAction=e()}(window,(function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=6)}([function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.BEACON_ATTA_REQUEST_URL=e.BEACON_ATTA_TOKEN=e.BEACON_ATTA_ID=e.BEACON_CONFIG_HTTPS_URL=e.BEACON_CONFIG_REQUEST_TIME=e.BEACON_CONFIG=e.BEACON_SENDING_IDS_KEY=e.BEACON_NORMAL_LOG_ID_KEY=e.BEACON_DRIECT_LOG_ID_KEY=e.BEACON_LASE_REPORT_TIME_KEY=e.BEACON_DEVICE_ID_KEY=e.BEACON_STORE_PREFIX=e.BEACON_LOG_ID_KEY=e.BEACON_IS_REALTIME_KEY=e.BEACON_DELAY_DEFAULT=e.BEACON_HTTPS_URL=e.BEACON_HTTP_URL=void 0,e.BEACON_HTTP_URL="http://oth.eve.mdt.qq.com:8080/analytics/v2_upload",e.BEACON_HTTPS_URL="https://otheve.beacon.qq.com/analytics/v2_upload",e.BEACON_DELAY_DEFAULT=3e3,e.BEACON_IS_REALTIME_KEY="A99",e.BEACON_LOG_ID_KEY="A100",e.BEACON_STORE_PREFIX="__BEACON_",e.BEACON_DEVICE_ID_KEY="__BEACON_deviceId",e.BEACON_LASE_REPORT_TIME_KEY="last_report_time",e.BEACON_DRIECT_LOG_ID_KEY="direct_log_id",e.BEACON_NORMAL_LOG_ID_KEY="normal_log_id",e.BEACON_SENDING_IDS_KEY="sending_event_ids",e.BEACON_CONFIG="beacon_config",e.BEACON_CONFIG_REQUEST_TIME="beacon_config_request_time",e.BEACON_CONFIG_HTTPS_URL="https://oth.str.beacon.qq.com/trpc.beacon.configserver.BeaconConfigService/QueryConfig",e.BEACON_ATTA_ID="00400014144",e.BEACON_ATTA_TOKEN="6478159937",e.BEACON_ATTA_REQUEST_URL="https://h.trace.qq.com/kv"},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.getEventId=e.replaceSymbol=e.replace=e.assert=void 0;var r=n(0),o=n(9);function i(t){if("string"!=typeof t)return t;try{return t.replace(new RegExp("\\|","g"),"%7C").replace(new RegExp("\\&","g"),"%26").replace(new RegExp("\\=","g"),"%3D").replace(new RegExp("\\+","g"),"%2B")}catch(t){return""}}Object.defineProperty(e,"EventEmiter",{enumerable:!0,get:function(){return o.EventEmiter}}),e.assert=function(t,e){if(!t)throw e instanceof Error?e:new Error(e)},e.replace=function(t,e){for(var n={},r=0,o=Object.keys(t);r60*this.strategy.requestInterval*1e3},t.prototype.getUploadUrl=function(){return this.strategy.httpsUploadUrl+"?appkey="+this.appkey},t.prototype.isBlackEvent=function(t){return-1!=this.strategy.blacklist.indexOf(t)},t.prototype.isEventUpOnOff=function(){return this.strategy.isEventUpOnOff},t.prototype.isSampleEvent=function(t){return!!Object.prototype.hasOwnProperty.call(this.realSample,t)&&this.realSample[t] { 32 | console.log(err || data); 33 | } 34 | ); 35 | } 36 | 37 | function uploadFile() { 38 | var uploadFile = function (file) { 39 | cos.uploadFile( 40 | { 41 | Bucket: config.Bucket, 42 | Region: config.Region, 43 | Key: file.name, 44 | FilePath: file.path, 45 | FileSize: file.size, 46 | SliceSize: 1024 * 1024 * 5, // 文件大于5mb自动使用分块上传 47 | onTaskReady: function (taskId) { 48 | TaskId = taskId; 49 | }, 50 | onProgress: function (info) { 51 | var percent = parseInt(info.percent * 10000) / 100; 52 | var speed = parseInt((info.speed / 1024 / 1024) * 100) / 100; 53 | console.log('进度:' + percent + '%; 速度:' + speed + 'Mb/s;'); 54 | }, 55 | onFileFinish: function (err, data, options) { 56 | console.log(options.Key + '上传' + (err ? '失败' : '完成')); 57 | }, 58 | }, 59 | function (err, data) { 60 | console.log(err || data); 61 | } 62 | ); 63 | }; 64 | wx.chooseMedia({ 65 | count: 1, 66 | mediaType: ['image', 'video'], 67 | sizeType: ['original'], 68 | sourceType: ['album', 'camera'], 69 | success: function (res) { 70 | const file = res.tempFiles[0]; 71 | const filePath = file.tempFilePath; 72 | const filename = filePath.substr(filePath.lastIndexOf('/') + 1); 73 | uploadFile({ 74 | name: filename, 75 | path: filePath, 76 | size: file.size, 77 | }); 78 | }, 79 | }); 80 | } 81 | 82 | function uploadFiles() { 83 | const uploadFiles = function (files) { 84 | const fileList = files.map(function (file) { 85 | const filePath = file.tempFilePath; 86 | const filename = filePath.substr(filePath.lastIndexOf('/') + 1); 87 | return Object.assign({ 88 | Bucket: config.Bucket, 89 | Region: config.Region, 90 | Key: filename, 91 | FilePath: filePath, 92 | }); 93 | }); 94 | cos.uploadFiles( 95 | { 96 | files: fileList, 97 | SliceSize: 1024 * 1024 * 5, // 文件大于5mb自动使用分块上传 98 | onProgress: function (info) { 99 | var percent = parseInt(info.percent * 10000) / 100; 100 | var speed = parseInt((info.speed / 1024 / 1024) * 100) / 100; 101 | console.log('进度:' + percent + '%; 速度:' + speed + 'Mb/s;'); 102 | }, 103 | onFileFinish: function (err, data, options) { 104 | console.log(options.Key + '上传' + (err ? '失败' : '完成')); 105 | }, 106 | }, 107 | function (err, data) { 108 | console.log(err || data); 109 | } 110 | ); 111 | }; 112 | wx.chooseMedia({ 113 | count: 9, 114 | mediaType: ['image', 'video'], 115 | sizeType: ['original'], 116 | sourceType: ['album', 'camera'], 117 | success: function (res) { 118 | uploadFiles(res.tempFiles); 119 | }, 120 | }); 121 | } 122 | 123 | function sliceUploadFile() { 124 | var sliceUploadFile = function (file) { 125 | const filePath = file.tempFilePath; 126 | const filename = filePath.substr(filePath.lastIndexOf('/') + 1); 127 | var key = filename; 128 | cos.sliceUploadFile( 129 | { 130 | Bucket: config.Bucket, 131 | Region: config.Region, 132 | Key: key, 133 | FilePath: filePath, 134 | FileSize: file.size, 135 | CacheControl: 'max-age=7200', 136 | Headers: { 137 | aa: 123, 138 | }, 139 | Query: { 140 | bb: 123, 141 | }, 142 | onTaskReady: function (taskId) { 143 | TaskId = taskId; 144 | }, 145 | onHashProgress: function (info) { 146 | console.log('check hash', JSON.stringify(info)); 147 | }, 148 | onProgress: function (info) { 149 | console.log(JSON.stringify(info)); 150 | }, 151 | }, 152 | requestCallback 153 | ); 154 | }; 155 | wx.chooseMedia({ 156 | count: 1, 157 | mediaType: ['image', 'video'], 158 | sizeType: ['original'], 159 | sourceType: ['album', 'camera'], 160 | success: function (res) { 161 | sliceUploadFile(res.tempFiles[0]); 162 | }, 163 | }); 164 | // wx.chooseVideo({ 165 | // sourceType: ['album','camera'], 166 | // maxDuration: 60, 167 | // camera: 'back', 168 | // success(res) { 169 | // var name = res.tempFilePath.replace(/^.*?([^/]{32}\.\w+)$/, '$1'); 170 | // sliceUploadFile({ 171 | // name: name, 172 | // path: res.tempFilePath, 173 | // size: res.size, 174 | // }); 175 | // }, 176 | // fail(err) { 177 | // console.log(err); 178 | // } 179 | // }) 180 | } 181 | 182 | function postObject() { 183 | wx.chooseMedia({ 184 | count: 1, // 默认9 185 | mediaType: ['image', 'video'], 186 | sizeType: ['original'], 187 | sourceType: ['album', 'camera'], 188 | success: function (res) { 189 | var file = res.tempFiles[0]; 190 | cos.postObject( 191 | { 192 | Bucket: config.Bucket, 193 | Region: config.Region, 194 | Key: '1.png', 195 | FilePath: file.tempFilePath, 196 | onTaskReady: function (taskId) { 197 | TaskId = taskId; 198 | }, 199 | onProgress: function (info) { 200 | console.log(JSON.stringify(info)); 201 | }, 202 | }, 203 | requestCallback 204 | ); 205 | }, 206 | }); 207 | } 208 | 209 | function putObject() { 210 | wx.chooseMedia({ 211 | count: 10, 212 | mediaType: ['image', 'video'], 213 | sizeType: ['original'], 214 | sourceType: ['album', 'camera'], 215 | success: function (res) { 216 | const file = res.tempFiles[0]; 217 | const filePath = file.tempFilePath; 218 | const filename = filePath.substr(filePath.lastIndexOf('/') + 1); 219 | wxfs.readFile({ 220 | filePath: filePath, 221 | success: function (res) { 222 | cos.putObject( 223 | { 224 | Bucket: config.Bucket, 225 | Region: config.Region, 226 | Key: filename, 227 | Body: res.data, // 在小程序里,putObject 接口只允许传字符串的内容,不支持 TaskReady 和 onProgress,上传请使用 cos.postObject 接口 228 | Headers: { 229 | // 万象持久化接口,上传时持久化。例子:通过 imageMogr2 接口使用图片缩放功能:指定图片宽度为 200,宽度等比压缩 230 | // 'Pic-Operations': '{"is_pic_info": 1, "rules": [{"fileid": "desample_photo.jpg", "rule": "imageMogr2/thumbnail/200x/"}]}' 231 | }, 232 | }, 233 | requestCallback 234 | ); 235 | }, 236 | fail: (err) => console.error(err), 237 | }); 238 | }, 239 | fail: (err) => console.error(err), 240 | }); 241 | } 242 | 243 | function putObjectStr() { 244 | cos.putObject( 245 | { 246 | Bucket: config.Bucket, 247 | Region: config.Region, 248 | Key: '1.txt', 249 | Body: 'hello world', // 在小程序里,putObject 接口只允许传字符串的内容,不支持 TaskReady 和 onProgress,上传请使用 cos.postObject 接口 250 | Headers: { 251 | aa: 123, 252 | }, 253 | Query: { 254 | bb: 123, 255 | }, 256 | }, 257 | requestCallback 258 | ); 259 | } 260 | 261 | function putObjectBase64() { 262 | var base64Url = 263 | '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=='; 264 | var m = /data:image\/(\w+);base64,(.*)/.exec(base64Url) || []; 265 | var format = m[1]; 266 | var bodyData = m[2]; 267 | var fileBuf = wx.base64ToArrayBuffer(bodyData); 268 | cos.putObject( 269 | { 270 | Bucket: config.Bucket, 271 | Region: config.Region, 272 | Key: '1.' + format, 273 | Body: fileBuf, 274 | }, 275 | requestCallback 276 | ); 277 | } 278 | 279 | function getObjectUrl() { 280 | var url = cos.getObjectUrl( 281 | { 282 | Bucket: config.Bucket, // Bucket 格式:test-1250000000 283 | Region: config.Region, 284 | Key: '1mb.zip', 285 | Expires: 60, 286 | Sign: true, 287 | }, 288 | function (err, data) { 289 | console.log(err || data); 290 | } 291 | ); 292 | console.log(url); 293 | } 294 | 295 | var toolsDao = { 296 | request: request, 297 | 'uploadFile 高级上传': uploadFile, 298 | 'uploadFiles 批量上传': uploadFiles, 299 | 'sliceUploadFile 分片上传': sliceUploadFile, 300 | // 上传文件适用于单请求上传大文件 301 | 'postObject 简单上传': postObject, 302 | 'putObject 简单上传文件': putObject, 303 | 'putObject 上传字符串': putObjectStr, 304 | // 上传文件 305 | 'putObject base64 转 ArrayBuffer 上传': putObjectBase64, 306 | 'getObjectUrl 获取对象访问url': getObjectUrl, 307 | }; 308 | 309 | var bucketDao = { 310 | // Service 311 | 'getService 获取存储桶列表': function () { 312 | cos.getService(requestCallback); 313 | }, 314 | // 简单 Bucket 操作 315 | 'putBucket 创建存储桶': function () { 316 | cos.putBucket( 317 | { 318 | Bucket: config.Bucket, 319 | Region: config.Region, 320 | }, 321 | requestCallback 322 | ); 323 | }, 324 | 'headBucket 检索存储桶及其权限': function () { 325 | cos.headBucket( 326 | { 327 | Bucket: config.Bucket, 328 | Region: config.Region, 329 | }, 330 | requestCallback 331 | ); 332 | }, 333 | 'deleteBucket 删除存储桶': function () { 334 | cos.deleteBucket( 335 | { 336 | Bucket: config.Bucket, 337 | Region: config.Region, 338 | }, 339 | requestCallback 340 | ); 341 | }, 342 | 'getBucketACL 查询存储桶 ACL': function () { 343 | cos.getBucketAcl( 344 | { 345 | Bucket: config.Bucket, 346 | Region: config.Region, 347 | }, 348 | requestCallback 349 | ); 350 | }, 351 | 'putBucketACL 设置存储桶 ACL': function () { 352 | cos.putBucketAcl( 353 | { 354 | Bucket: config.Bucket, 355 | Region: config.Region, 356 | ACL: 'public-read', 357 | }, 358 | requestCallback 359 | ); 360 | }, 361 | 'getBucketCors 查询跨域配置': function () { 362 | cos.getBucketCors( 363 | { 364 | Bucket: config.Bucket, 365 | Region: config.Region, 366 | }, 367 | requestCallback 368 | ); 369 | }, 370 | 'putBucketCors 设置跨域配置': function () { 371 | cos.putBucketCors( 372 | { 373 | Bucket: config.Bucket, 374 | Region: config.Region, 375 | ResponseVary: 'true', 376 | CORSRules: [ 377 | { 378 | AllowedOrigin: ['*'], 379 | AllowedMethod: ['GET', 'POST', 'PUT', 'DELETE', 'HEAD'], 380 | AllowedHeader: ['*'], 381 | ExposeHeader: ['ETag', 'Content-Length'], 382 | MaxAgeSeconds: '5', 383 | }, 384 | ], 385 | }, 386 | requestCallback 387 | ); 388 | }, 389 | 'deleteBucketCors 删除跨域配置': function () { 390 | cos.deleteBucketCors( 391 | { 392 | Bucket: config.Bucket, 393 | Region: config.Region, 394 | }, 395 | requestCallback 396 | ); 397 | }, 398 | 'putBucketPolicy 设置存储桶策略': function () { 399 | var AppId = config.Bucket.substr(config.Bucket.lastIndexOf('-') + 1); 400 | cos.putBucketPolicy( 401 | { 402 | Bucket: config.Bucket, 403 | Region: config.Region, 404 | Policy: { 405 | version: '2.0', 406 | principal: { 407 | qcs: ['qcs::cam::uin/10001:uin/10001'], 408 | }, // 这里的 10001 是 QQ 号 409 | statement: [ 410 | { 411 | effect: 'allow', 412 | action: [ 413 | 'name/cos:GetBucket', 414 | 'name/cos:PutObject', 415 | 'name/cos:PostObject', 416 | 'name/cos:PutObjectCopy', 417 | 'name/cos:InitiateMultipartUpload', 418 | 'name/cos:UploadPart', 419 | 'name/cos:UploadPartCopy', 420 | 'name/cos:CompleteMultipartUpload', 421 | 'name/cos:AbortMultipartUpload', 422 | 'name/cos:AppendObject', 423 | ], 424 | // "resource": ["qcs::cos:ap-guangzhou:uid/1250000000:test-1250000000/*"] // 1250000000 是 appid 425 | resource: ['qcs::cos:' + config.Region + ':uid/' + AppId + ':' + config.Bucket + '/*'], // 1250000000 是 appid 426 | }, 427 | ], 428 | }, 429 | }, 430 | requestCallback 431 | ); 432 | }, 433 | 'getBucketPolicy 查询存储桶策略': function () { 434 | cos.getBucketPolicy( 435 | { 436 | Bucket: config.Bucket, 437 | Region: config.Region, 438 | }, 439 | requestCallback 440 | ); 441 | }, 442 | 'deleteBucketPolicy 删除存储桶策略': function () { 443 | cos.deleteBucketPolicy( 444 | { 445 | Bucket: config.Bucket, 446 | Region: config.Region, 447 | }, 448 | requestCallback 449 | ); 450 | }, 451 | 'getBucketLocation 获取Bucket的地域信息': function () { 452 | cos.getBucketLocation( 453 | { 454 | Bucket: config.Bucket, 455 | Region: config.Region, 456 | }, 457 | requestCallback 458 | ); 459 | }, 460 | 'getBucketTagging 获取Bucket标签': function () { 461 | cos.getBucketTagging( 462 | { 463 | Bucket: config.Bucket, 464 | Region: config.Region, 465 | }, 466 | requestCallback 467 | ); 468 | }, 469 | 'putBucketTagging 设置Bucket标签': function () { 470 | cos.putBucketTagging( 471 | { 472 | Bucket: config.Bucket, 473 | Region: config.Region, 474 | Tagging: { 475 | Tags: [ 476 | { 477 | Key: 'k1', 478 | Value: 'v1', 479 | }, 480 | { 481 | Key: 'k2', 482 | Value: 'v2', 483 | }, 484 | ], 485 | }, 486 | }, 487 | requestCallback 488 | ); 489 | }, 490 | 'deleteBucketTagging 删除存储桶标签': function () { 491 | cos.deleteBucketTagging( 492 | { 493 | Bucket: config.Bucket, 494 | Region: config.Region, 495 | }, 496 | requestCallback 497 | ); 498 | }, 499 | }; 500 | 501 | var objectDao = { 502 | 'getBucket 获取对象列表': function () { 503 | cos.getBucket( 504 | { 505 | Bucket: config.Bucket, 506 | Region: config.Region, 507 | }, 508 | requestCallback 509 | ); 510 | }, 511 | // 上传文件适用于单请求上传大文件 512 | 'postObject 表单上传对象': postObject, 513 | 'putObject 简单上传文件': putObject, 514 | 'putObject 上传字符串': putObjectStr, 515 | // 上传文件 516 | 'putObject base64 转 ArrayBuffer 上传': putObjectBase64, 517 | 'getObject 下载对象': function () { 518 | cos.getObject( 519 | { 520 | Bucket: config.Bucket, 521 | Region: config.Region, 522 | Key: '1.png', 523 | }, 524 | function (err, data) { 525 | console.log('getObject:', err || data); 526 | } 527 | ); 528 | }, 529 | 'abortUploadTask 抛弃分块上传任务': function () { 530 | cos.abortUploadTask( 531 | { 532 | Bucket: config.Bucket, 533 | /* 必须 */ // Bucket 格式:test-1250000000 534 | Region: config.Region, 535 | /* 必须 */ 536 | // 格式1,删除单个上传任务 537 | // Level: 'task', 538 | // Key: '10mb.zip', 539 | // UploadId: '14985543913e4e2642e31db217b9a1a3d9b3cd6cf62abfda23372c8d36ffa38585492681e3', 540 | // 格式2,删除单个文件所有未完成上传任务 541 | Level: 'file', 542 | Key: '10mb.zip', 543 | // 格式3,删除 Bucket 下所有未完成上传任务 544 | // Level: 'bucket', 545 | }, 546 | requestCallback 547 | ); 548 | }, 549 | 'deleteObject 删除文件': function () { 550 | cos.deleteObject( 551 | { 552 | Bucket: config.Bucket, 553 | Region: config.Region, 554 | Key: '1.zip', 555 | }, 556 | requestCallback 557 | ); 558 | }, 559 | }; 560 | 561 | var advanceObjectDao = { 562 | 'sliceUploadFile 分块上传': function () { 563 | var sliceUploadFile = function (file) { 564 | const filePath = file.tempFilePath; 565 | const filename = filePath.substr(filePath.lastIndexOf('/') + 1); 566 | var key = filename; 567 | cos.sliceUploadFile( 568 | { 569 | Bucket: config.Bucket, 570 | Region: config.Region, 571 | Key: key, 572 | FilePath: filePath, 573 | FileSize: file.size, 574 | CacheControl: 'max-age=7200', 575 | Headers: { 576 | aa: 123, 577 | }, 578 | Query: { 579 | bb: 123, 580 | }, 581 | onTaskReady: function (taskId) { 582 | TaskId = taskId; 583 | }, 584 | onHashProgress: function (info) { 585 | console.log('check hash', JSON.stringify(info)); 586 | }, 587 | onProgress: function (info) { 588 | console.log(JSON.stringify(info)); 589 | }, 590 | }, 591 | requestCallback 592 | ); 593 | }; 594 | wx.chooseMedia({ 595 | count: 1, 596 | mediaType: ['image', 'video'], 597 | sizeType: ['original'], 598 | sourceType: ['album', 'camera'], 599 | success: function (res) { 600 | sliceUploadFile(res.tempFiles[0]); 601 | }, 602 | }); 603 | // wx.chooseVideo({ 604 | // sourceType: ['album','camera'], 605 | // maxDuration: 60, 606 | // camera: 'back', 607 | // success(res) { 608 | // var name = res.tempFilePath.replace(/^.*?([^/]{32}\.\w+)$/, '$1'); 609 | // sliceUploadFile({ 610 | // name: name, 611 | // path: res.tempFilePath, 612 | // size: res.size, 613 | // }); 614 | // }, 615 | // fail(err) { 616 | // console.log(err); 617 | // } 618 | // }) 619 | }, 620 | 'sliceCopyFile 分块复制对象': function () { 621 | // 创建测试文件 622 | var sourceName = '1.txt'; 623 | var Key = '1.slicecopy.exe'; 624 | 625 | var sourcePath = config.Bucket + '.cos.' + config.Region + '.myqcloud.com/' + sourceName; 626 | 627 | cos.sliceCopyFile( 628 | { 629 | Bucket: config.Bucket, // Bucket 格式:test-1250000000 630 | Region: config.Region, 631 | Key: Key, 632 | CopySource: sourcePath, 633 | SliceSize: 20 * 1024 * 1024, // 大于20M的文件用分片复制,小于则用单片复制 634 | onProgress: function (info) { 635 | var percent = parseInt(info.percent * 10000) / 100; 636 | var speed = parseInt((info.speed / 1024 / 1024) * 100) / 100; 637 | console.log('进度:' + percent + '%; 速度:' + speed + 'Mb/s;'); 638 | }, 639 | }, 640 | requestCallback 641 | ); 642 | }, 643 | cancelTask: function () { 644 | cos.cancelTask(TaskId); 645 | console.log('canceled'); 646 | }, 647 | pauseTask: function () { 648 | cos.pauseTask(TaskId); 649 | console.log('paused'); 650 | }, 651 | restartTask: function () { 652 | cos.restartTask(TaskId); 653 | console.log('restart'); 654 | }, 655 | }; 656 | 657 | // require('./test'); 658 | 659 | module.exports = { 660 | toolsDao: toolsDao, 661 | bucketDao: bucketDao, 662 | objectDao: objectDao, 663 | advanceObjectDao: advanceObjectDao, 664 | }; 665 | --------------------------------------------------------------------------------