From de4417b95431110b449f245b2064e782bfb4d8a4 Mon Sep 17 00:00:00 2001 From: yanglbme Date: Sun, 8 Nov 2020 20:24:30 +0800 Subject: [PATCH 1/4] feat: support qiniu kudo --- src/api/file.js | 51 ++++++ src/assets/scripts/tokenTools.js | 155 ++++++++++++++++++ .../CodemirrorEditor/uploadImgDialog.vue | 95 +++++++++++ 3 files changed, 301 insertions(+) create mode 100644 src/assets/scripts/tokenTools.js diff --git a/src/api/file.js b/src/api/file.js index ba74498..a9196f2 100644 --- a/src/api/file.js +++ b/src/api/file.js @@ -1,8 +1,11 @@ import fetch from "./fetch"; +import CryptoJS from "crypto-js"; import OSS from "ali-oss"; import COS from "cos-js-sdk-v5"; import Buffer from "buffer-from"; import { v4 as uuidv4 } from "uuid"; +import * as qiniu from "qiniu-js"; +import { utf16to8, base64encode, safe64 } from "../assets/scripts/tokenTools"; const defaultConfig = { username: "filess", @@ -28,6 +31,8 @@ function fileUpload(content, file) { return aliOSSFileUpload(content, file.name); case "txCOS": return txCOSFileUpload(file); + case "qiniu": + return qiniuUpload(file); case "github": default: return ghFileUpload(content, file.name); @@ -81,6 +86,14 @@ function getGitHubConfig() { ); } +function getQiniuToken(accessKey, secretKey, putPolicy) { + const policy = JSON.stringify(putPolicy); + const encoded = base64encode(utf16to8(policy)); + const hash = CryptoJS.HmacSHA1(encoded, secretKey); + const encodedSigned = hash.toString(CryptoJS.enc.Base64); + return accessKey + ":" + safe64(encodedSigned) + ":" + encoded; +} + async function ghFileUpload(content, filename) { const isDefault = localStorage.getItem("imgHost") !== "github"; const config = isDefault ? getDefaultConfig() : getGitHubConfig(); @@ -166,6 +179,44 @@ async function txCOSFileUpload(file) { }); } +async function qiniuUpload(file) { + const qiniuConfig = JSON.parse(localStorage.getItem("qiniuConfig")); + const putPolicy = { + scope: qiniuConfig.bucket, + deadline: Math.trunc(new Date().getTime() / 1000) + 3600, + }; + const token = getQiniuToken( + qiniuConfig.accessKey, + qiniuConfig.secretKey, + putPolicy + ); + const dir = qiniuConfig.path ? qiniuConfig.path + "/" : ""; + const dateFilename = + dir + + new Date().getTime() + + "-" + + uuidv4() + + "." + + file.name.split(".")[1]; + const config = { + region: qiniuConfig.region, + }; + const observable = qiniu.upload(file, dateFilename, token, {}, config); + return new Promise((resolve, reject) => { + observable.subscribe({ + next: (result) => { + console.log(result); + }, + error: (err) => { + reject(err.message); + }, + complete: (result) => { + resolve(qiniuConfig.domain + "/" + result.key); + }, + }); + }); +} + export default { fileUpload, }; diff --git a/src/assets/scripts/tokenTools.js b/src/assets/scripts/tokenTools.js new file mode 100644 index 0000000..d7868b7 --- /dev/null +++ b/src/assets/scripts/tokenTools.js @@ -0,0 +1,155 @@ +/* utf.js - UTF-8 <=> UTF-16 convertion + * + * Copyright (C) 1999 Masanao Izumo + * Version: 1.0 + * LastModified: Dec 25 1999 + * This library is free. You can redistribute it and/or modify it. + */ +/* + * Interfaces: + * utf8 = utf16to8(utf16); + * utf16 = utf8to16(utf8); + */ + +export function utf16to8(str) { + var out, i, len, c; + out = ""; + len = str.length; + for (i = 0; i < len; i++) { + c = str.charCodeAt(i); + if ((c >= 0x0001) && (c <= 0x007F)) { + out += str.charAt(i); + } else if (c > 0x07FF) { + out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F)); + out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F)); + out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F)); + } else { + out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F)); + out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F)); + } + } + return out; +} + +export function utf8to16(str) { + var out, i, len, c; + var char2, char3; + out = ""; + len = str.length; + i = 0; + while (i < len) { + c = str.charCodeAt(i++); + switch (c >> 4) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // 0xxxxxxx + out += str.charAt(i - 1); + break; + case 12: + case 13: + // 110x xxxx 10xx xxxx + char2 = str.charCodeAt(i++); + out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = str.charCodeAt(i++); + char3 = str.charCodeAt(i++); + out += String.fromCharCode(((c & 0x0F) << 12) + | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); + break; + } + } + return out; +} +//======================================================== +/* + * Interfaces: + * b64 = base64encode(data); + * data = base64decode(b64); + */ +var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; +var base64DecodeChars = new Array(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1); +export function base64encode(str) { + var out, i, len; + var c1, c2, c3; + len = str.length; + i = 0; + out = ""; + while (i < len) { + c1 = str.charCodeAt(i++) & 0xff; + if (i == len) { + out += base64EncodeChars.charAt(c1 >> 2); + out += base64EncodeChars.charAt((c1 & 0x3) << 4); + out += "=="; + break; + } + c2 = str.charCodeAt(i++); + if (i == len) { + out += base64EncodeChars.charAt(c1 >> 2); + out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); + out += base64EncodeChars.charAt((c2 & 0xF) << 2); + out += "="; + break; + } + c3 = str.charCodeAt(i++); + out += base64EncodeChars.charAt(c1 >> 2); + out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); + out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)); + out += base64EncodeChars.charAt(c3 & 0x3F); + } + return out; +} + +export function base64decode(str) { + var c1, c2, c3, c4; + var i, len, out; + len = str.length; + i = 0; + out = ""; + while (i < len) { + /* c1 */ + do { + c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff]; + } while (i < len && c1 == -1); + if (c1 == -1) break; + /* c2 */ + do { + c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff]; + } while (i < len && c2 == -1); + if (c2 == -1) break; + out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4)); + /* c3 */ + do { + c3 = str.charCodeAt(i++) & 0xff; + if (c3 == 61) return out; + c3 = base64DecodeChars[c3]; + } while (i < len && c3 == -1); + if (c3 == -1) break; + out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2)); + /* c4 */ + do { + c4 = str.charCodeAt(i++) & 0xff; + if (c4 == 61) return out; + c4 = base64DecodeChars[c4]; + } while (i < len && c4 == -1); + if (c4 == -1) break; + out += String.fromCharCode(((c3 & 0x03) << 6) | c4); + } + return out; +} + +export function safe64(base64) { + base64 = base64.replace(/\+/g, "-"); + base64 = base64.replace(/\//g, "_"); + return base64; +} \ No newline at end of file diff --git a/src/components/CodemirrorEditor/uploadImgDialog.vue b/src/components/CodemirrorEditor/uploadImgDialog.vue index d57ee6c..9be7dcb 100644 --- a/src/components/CodemirrorEditor/uploadImgDialog.vue +++ b/src/components/CodemirrorEditor/uploadImgDialog.vue @@ -201,6 +201,66 @@ + + + + + + + + + + + + + + + + + + + + 如何使用七牛云 Kodo? + + + 保存配置 + + + @@ -238,6 +298,13 @@ export default { path: "", cdnHost: "", }, + formQiniu: { + accessKey: "", + secretKey: "", + bucket: "", + domain: "", + region: "", + }, options: [ { value: "default", @@ -255,6 +322,10 @@ export default { value: "txCOS", label: "腾讯云", }, + { + value: "qiniu", + label: "七牛云", + }, ], imgHost: "default", uploadingImg: false, @@ -353,6 +424,30 @@ export default { }); }, + saveQiniuConfiguration() { + if ( + !( + this.formQiniu.accessKey && + this.formQiniu.secretKey && + this.formQiniu.bucket && + this.formQiniu.domain && + this.formQiniu.region + ) + ) { + this.$message({ + showClose: true, + message: `七牛云 Kodo 参数配置不全`, + type: "error", + }); + return; + } + localStorage.setItem("qiniuConfig", JSON.stringify(this.formQiniu)); + this.$message({ + message: "保存成功", + type: "success", + }); + }, + // 图片上传前的处理 beforeUpload(file) { if (!this.validateConfig()) { From aec762f237176717dee087eca392441173d9c1b1 Mon Sep 17 00:00:00 2001 From: yanglbme Date: Sun, 8 Nov 2020 12:28:26 +0000 Subject: [PATCH 2/4] docs: prettify code --- src/assets/scripts/tokenTools.js | 266 +++++++++++++----- .../CodemirrorEditor/uploadImgDialog.vue | 10 +- 2 files changed, 204 insertions(+), 72 deletions(-) diff --git a/src/assets/scripts/tokenTools.js b/src/assets/scripts/tokenTools.js index d7868b7..2f76c57 100644 --- a/src/assets/scripts/tokenTools.js +++ b/src/assets/scripts/tokenTools.js @@ -10,63 +10,66 @@ * utf8 = utf16to8(utf16); * utf16 = utf8to16(utf8); */ - + export function utf16to8(str) { - var out, i, len, c; - out = ""; - len = str.length; - for (i = 0; i < len; i++) { - c = str.charCodeAt(i); - if ((c >= 0x0001) && (c <= 0x007F)) { - out += str.charAt(i); - } else if (c > 0x07FF) { - out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F)); - out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F)); - out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F)); - } else { - out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F)); - out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F)); - } - } - return out; + var out, i, len, c; + out = ""; + len = str.length; + for (i = 0; i < len; i++) { + c = str.charCodeAt(i); + if (c >= 0x0001 && c <= 0x007f) { + out += str.charAt(i); + } else if (c > 0x07ff) { + out += String.fromCharCode(0xe0 | ((c >> 12) & 0x0f)); + out += String.fromCharCode(0x80 | ((c >> 6) & 0x3f)); + out += String.fromCharCode(0x80 | ((c >> 0) & 0x3f)); + } else { + out += String.fromCharCode(0xc0 | ((c >> 6) & 0x1f)); + out += String.fromCharCode(0x80 | ((c >> 0) & 0x3f)); + } + } + return out; } - + export function utf8to16(str) { - var out, i, len, c; - var char2, char3; - out = ""; - len = str.length; - i = 0; - while (i < len) { - c = str.charCodeAt(i++); - switch (c >> 4) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - // 0xxxxxxx - out += str.charAt(i - 1); - break; - case 12: - case 13: - // 110x xxxx 10xx xxxx - char2 = str.charCodeAt(i++); - out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); - break; - case 14: - // 1110 xxxx 10xx xxxx 10xx xxxx - char2 = str.charCodeAt(i++); - char3 = str.charCodeAt(i++); - out += String.fromCharCode(((c & 0x0F) << 12) - | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); - break; - } - } - return out; + var out, i, len, c; + var char2, char3; + out = ""; + len = str.length; + i = 0; + while (i < len) { + c = str.charCodeAt(i++); + switch (c >> 4) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // 0xxxxxxx + out += str.charAt(i - 1); + break; + case 12: + case 13: + // 110x xxxx 10xx xxxx + char2 = str.charCodeAt(i++); + out += String.fromCharCode(((c & 0x1f) << 6) | (char2 & 0x3f)); + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = str.charCodeAt(i++); + char3 = str.charCodeAt(i++); + out += String.fromCharCode( + ((c & 0x0f) << 12) | + ((char2 & 0x3f) << 6) | + ((char3 & 0x3f) << 0) + ); + break; + } + } + return out; } //======================================================== /* @@ -74,11 +77,138 @@ export function utf8to16(str) { * b64 = base64encode(data); * data = base64decode(b64); */ -var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; -var base64DecodeChars = new Array(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1); +var base64EncodeChars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; +var base64DecodeChars = new Array( + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 62, + -1, + -1, + -1, + 63, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + -1, + -1, + -1, + -1, + -1, + -1, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + -1, + -1, + -1, + -1, + -1 +); export function base64encode(str) { var out, i, len; var c1, c2, c3; @@ -96,20 +226,22 @@ export function base64encode(str) { c2 = str.charCodeAt(i++); if (i == len) { out += base64EncodeChars.charAt(c1 >> 2); - out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); - out += base64EncodeChars.charAt((c2 & 0xF) << 2); + out += base64EncodeChars.charAt( + ((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4) + ); + out += base64EncodeChars.charAt((c2 & 0xf) << 2); out += "="; break; } c3 = str.charCodeAt(i++); out += base64EncodeChars.charAt(c1 >> 2); - out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); - out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)); - out += base64EncodeChars.charAt(c3 & 0x3F); + out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xf0) >> 4)); + out += base64EncodeChars.charAt(((c2 & 0xf) << 2) | ((c3 & 0xc0) >> 6)); + out += base64EncodeChars.charAt(c3 & 0x3f); } return out; } - + export function base64decode(str) { var c1, c2, c3, c4; var i, len, out; @@ -135,7 +267,7 @@ export function base64decode(str) { c3 = base64DecodeChars[c3]; } while (i < len && c3 == -1); if (c3 == -1) break; - out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2)); + out += String.fromCharCode(((c2 & 0xf) << 4) | ((c3 & 0x3c) >> 2)); /* c4 */ do { c4 = str.charCodeAt(i++) & 0xff; @@ -152,4 +284,4 @@ export function safe64(base64) { base64 = base64.replace(/\+/g, "-"); base64 = base64.replace(/\//g, "_"); return base64; -} \ No newline at end of file +} diff --git a/src/components/CodemirrorEditor/uploadImgDialog.vue b/src/components/CodemirrorEditor/uploadImgDialog.vue index 9be7dcb..375f42c 100644 --- a/src/components/CodemirrorEditor/uploadImgDialog.vue +++ b/src/components/CodemirrorEditor/uploadImgDialog.vue @@ -240,7 +240,7 @@ placeholder="如:z2" > - + Date: Sun, 8 Nov 2020 20:33:52 +0800 Subject: [PATCH 3/4] feat: update dependency package and README document --- README.md | 1 + package.json | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1a32d42..b78a21b 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ Markdown 文档自动即时渲染为微信图文,让你不再为微信文章 | 2 | GitHub 图床 | 配置 `Repo`、`Token` 参数 | [如何获取 GitHub token?](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) | | 3 | 阿里云 OSS | 配置 `AccessKey ID`、`AccessKey Secret`、`Bucket`、`Region` 等参数 | [如何使用阿里云 OSS?](https://help.aliyun.com/document_detail/31883.html) | | 4 | 腾讯云 COS | 配置 `SecretId`、`SecretKey`、`Bucket`、`Region` 等参数 | [如何使用腾讯云 COS?](https://cloud.tencent.com/document/product/436/38484) | +| 5 | 七牛云 Kudo | 配置 `AccessKey`、`SecretKey`、`Bucket`、`Domain`、`Region` 等参数 | [如何使用七牛云 Kodo?](https://cloud.tencent.com/document/product/436/38484) | ![select-and-change-color-theme](./public/assets/images/select-and-change-color-theme.gif) diff --git a/package.json b/package.json index bc2a991..52f4db7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vue-md", "author": "doocs", - "version": "1.4.1", + "version": "1.4.2", "private": true, "homepage": "https://doocs.gitee.io/md", "scripts": { @@ -17,12 +17,14 @@ "codemirror": "^5.50.2", "core-js": "^3.4.4", "cos-js-sdk-v5": "^0.5.27", + "crypto-js": "^4.0.0", "element-ui": "^2.13.0", "jquery": "^3.4.1", "juice": "^6.0.0", "marked": "^0.8.0", "prettier": "^2.0.5", "prettify": "^0.1.7", + "qiniu-js": "^3.1.2", "uuid": "^8.3.0", "vue": "^2.6.10", "vue-router": "^3.1.3", From 41b4be1c910cff304292e8c230b3908ae56f3a01 Mon Sep 17 00:00:00 2001 From: yanglbme Date: Sun, 8 Nov 2020 12:36:10 +0000 Subject: [PATCH 4/4] docs: prettify code --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b78a21b..cb56905 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Markdown 文档自动即时渲染为微信图文,让你不再为微信文章 | 2 | GitHub 图床 | 配置 `Repo`、`Token` 参数 | [如何获取 GitHub token?](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) | | 3 | 阿里云 OSS | 配置 `AccessKey ID`、`AccessKey Secret`、`Bucket`、`Region` 等参数 | [如何使用阿里云 OSS?](https://help.aliyun.com/document_detail/31883.html) | | 4 | 腾讯云 COS | 配置 `SecretId`、`SecretKey`、`Bucket`、`Region` 等参数 | [如何使用腾讯云 COS?](https://cloud.tencent.com/document/product/436/38484) | -| 5 | 七牛云 Kudo | 配置 `AccessKey`、`SecretKey`、`Bucket`、`Domain`、`Region` 等参数 | [如何使用七牛云 Kodo?](https://cloud.tencent.com/document/product/436/38484) | +| 5 | 七牛云 Kudo | 配置 `AccessKey`、`SecretKey`、`Bucket`、`Domain`、`Region` 等参数 | [如何使用七牛云 Kodo?](https://cloud.tencent.com/document/product/436/38484) | ![select-and-change-color-theme](./public/assets/images/select-and-change-color-theme.gif)