feat: support qiniu kodo

This commit is contained in:
yanglbme 2020-11-08 20:24:30 +08:00
parent 5ae6fd4b28
commit 3fac278a83
5 changed files with 419 additions and 1 deletions

View File

@ -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 | 七牛云 Kodo | 配置 `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)

View File

@ -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",

View File

@ -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,
};

View File

@ -0,0 +1,269 @@
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;
}
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;
}

View File

@ -201,6 +201,66 @@
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane class="github-panel" label="七牛云 Kodo" name="qiniu">
<el-form
class="setting-form"
ref="form"
:model="formQiniu"
label-position="right"
label-width="140px"
>
<el-form-item label="AccessKey" :required="true">
<el-input
v-model.trim="formQiniu.accessKey"
placeholder="如6DD3VaLJ_SQgOdoocsyTV_YWaDmdnL2n8EGx7kG"
></el-input>
</el-form-item>
<el-form-item label="SecretKey" :required="true">
<el-input
v-model.trim="formQiniu.secretKey"
show-password
placeholder="如qgZa5qrvDOOcsmdKStD1oCjZ9nB7MDvJUs_34SIm"
></el-input>
</el-form-item>
<el-form-item label="Bucket" :required="true">
<el-input
v-model.trim="formQiniu.bucket"
placeholder="如md"
></el-input>
</el-form-item>
<el-form-item label="Bucket 对应域名" :required="true">
<el-input
v-model.trim="formQiniu.domain"
placeholder="如http://images.123ylb.cn"
></el-input>
</el-form-item>
<el-form-item label="存储区域" :required="true">
<el-input
v-model.trim="formQiniu.region"
placeholder="如z2"
></el-input>
</el-form-item>
<el-form-item label="存储路径" :required="false">
<el-input
v-model.trim="formQiniu.path"
placeholder="如img可不填默认为根目录"
></el-input>
<el-link
type="primary"
href="https://cloud.tencent.com/document/product/436/38484"
target="_blank"
>如何使用七牛云 Kodo</el-link
>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="saveQiniuConfiguration"
>保存配置</el-button
>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</el-dialog>
</template>
@ -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()) {