Merge pull request #31 from doocs/feature-upload-config

Feature upload config
This commit is contained in:
Yang Libin 2020-09-13 21:43:40 +08:00 committed by GitHub
commit 6ecc66a651
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 203 additions and 108 deletions

View File

@ -11,7 +11,9 @@
"test:unit": "vue-cli-service test:unit" "test:unit": "vue-cli-service test:unit"
}, },
"dependencies": { "dependencies": {
"ali-oss": "^6.10.0",
"axios": "^0.19.1", "axios": "^0.19.1",
"buffer-from": "^1.1.1",
"codemirror": "^5.50.2", "codemirror": "^5.50.2",
"core-js": "^3.4.4", "core-js": "^3.4.4",
"element-ui": "^2.13.0", "element-ui": "^2.13.0",

View File

@ -1,32 +1,108 @@
import fetch from './fetch'; import fetch from './fetch';
import store from '../store/index'; import OSS from 'ali-oss';
import Buffer from 'buffer-from';
import { import {
v4 as uuidv4 v4 as uuidv4
} from 'uuid'; } from 'uuid';
function fileUpload(content, filename) { const defaultConfig = {
const { username: 'filess',
method, repo: 'images',
token, method: 'put',
url accessToken: [
} = store.getters['imageApi/config']; '7715d7ca67b5d3837cfdoocsmde8c38421815aa423510af',
const uuid = uuidv4(); 'c411415bf95dbe39625doocsmd5047ba9b7a2a6c9642abe',
const dateFilename = new Date().getTime() + '-' + uuid + '.' + filename.split('.')[1]; '2821cd8819fa345c053doocsmdca86ac653f8bc20db1f1b',
const uploadUrl = url + dateFilename; '445f0dae46ef1f2a4d6doocsmdc797301e94797b4750a4c',
'cc1d0c1426d0fd0902bdoocsmdd2d7184b14da61b86ec46',
'b67e9d15cb6f910492fdoocsmdac6b44d379c953bb19eff',
'618c4dc2244ccbbc088doocsmd125d17fd31b7d06a50cf3',
'a4b581732e1c1507458doocsmdc5b223b27dae5e2e16a55'
]
}
return fetch({ function fileUpload(content, filename) {
url: uploadUrl, const imgHost = localStorage.getItem('imgHost');
method, !imgHost && localStorage.setItem('imgHost', 'default');
switch (imgHost) {
case 'aliOSS':
return aliOSSUploadFile(content, filename);
case 'github':
default:
return githubUploadFile(content, filename);
}
}
function getDefaultConfig() {
const date = new Date();
const dir = date.getFullYear() + '/' + (date.getMonth() + 1).toString().padStart(2, '0') + '/' + date.getDate().toString().padStart(2, '0');
const token = defaultConfig.accessToken[Math.floor(Math.random() * defaultConfig.accessToken.length)].replace('doocsmd', '');
return {
method: defaultConfig.method,
headers: { headers: {
'Authorization': 'token ' + token 'Authorization': 'token ' + token
}, },
data: { url: `https://api.github.com/repos/${defaultConfig.username}/${defaultConfig.repo}/contents/${dir}/`
message: 'Upload image by https://doocs.github.io/md', };
content: content
}
})
} }
function getGitHubConfig() {
const date = new Date();
const dir = date.getFullYear() + '/' + (date.getMonth() + 1).toString().padStart(2, '0') + '/' + date.getDate().toString().padStart(2, '0');
const githubConfig = JSON.parse(localStorage.getItem("githubConfig"));
const repoUrl = githubConfig.repo.replace("https://github.com/", "").replace("http://github.com/", "").replace("github.com/", "").split("/");
const username = repoUrl[0];
const repo = repoUrl[1];
return {
method: 'put',
headers: {
'Authorization': 'token ' + githubConfig.accessToken
},
url: `https://api.github.com/repos/${username}/${repo}/contents/${dir}/`
};
}
function githubUploadFile(content, filename) {
const isDefault = localStorage.getItem('imgHost') !== 'github';
const config = isDefault ? getDefaultConfig() : getGitHubConfig();
const dateFilename = new Date().getTime() + '-' + uuidv4() + '.' + filename.split('.')[1];
return fetch({
url: config.url + dateFilename,
method: config.method,
headers: config.headers,
data: {
message: 'Upload by https://doocs.github.io/md',
content: content
}
}).then(res => {
const githubResourceUrl = 'raw.githubusercontent.com/filess/images/master/';
const cdnResourceUrl = 'cdn.jsdelivr.net/gh/filess/images/';
return isDefault ? res.content.download_url.replace(githubResourceUrl, cdnResourceUrl) : res.content.download_url;
});
}
function aliOSSUploadFile(content, filename) {
const dateFilename = new Date().getTime() + '-' + uuidv4() + '.' + filename.split('.')[1];
const aliOSSConfig = JSON.parse(localStorage.getItem('aliOSSConfig'));
const buffer = Buffer(content, 'base64');
try {
const dir = aliOSSConfig.path + '/' + dateFilename;
const client = new OSS({
region: aliOSSConfig.region,
bucket: aliOSSConfig.bucket,
accessKeyId: aliOSSConfig.accessKeyId,
accessKeySecret: aliOSSConfig.accessKeySecret
});
return client.put(dir, buffer).then(res => {
return res.url;
})
} catch (e) {
return Promise.reject(e);
}
}
export default { export default {
fileUpload fileUpload

View File

@ -1,26 +1,22 @@
import fileApi from '../../api/file'; import fileApi from '../../api/file';
const githubResourceUrl = 'raw.githubusercontent.com/filess/images/master/';
const cdnResourceUrl = 'cdn.jsdelivr.net/gh/filess/images/';
export function uploadImgFile(file) { export function uploadImgFile(file) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const checkImageResult = isImageIllegal(file); const checkImageResult = isImageIllegal(file);
if (checkImageResult) { if (checkImageResult) {
reject(checkImageResult); reject(checkImageResult);
return; return;
} }
const imgFile = new FileReader(); const base64Reader = new FileReader();
imgFile.readAsDataURL(file); base64Reader.readAsDataURL(file);
imgFile.onload = function () { base64Reader.onload = function () {
const base64Content = this.result.split(',').pop(); const base64Content = this.result.split(',').pop();
fileApi.fileUpload(base64Content, file.name).then(res => { fileApi.fileUpload(base64Content, file.name).then(res => {
const imageUrl = res.content.download_url.replace(githubResourceUrl, cdnResourceUrl); resolve(res);
resolve(imageUrl);
}).catch(err => { }).catch(err => {
reject(err.message) reject(err);
}) })
} }
}); });

View File

@ -1,5 +1,5 @@
<template> <template>
<el-dialog title="插入图片" class="upload__dialog" :visible="value" @close="$emit('close')"> <el-dialog title="本地上传" class="upload__dialog" :visible="value" @close="$emit('close')">
<el-tabs type="card" :value="'upload'"> <el-tabs type="card" :value="'upload'">
<el-tab-pane class="upload-panel" label="选择上传" name="upload"> <el-tab-pane class="upload-panel" label="选择上传" name="upload">
<el-select v-model="imgHost" @change="changeImgHost" placeholder="请选择" size="small"> <el-select v-model="imgHost" @change="changeImgHost" placeholder="请选择" size="small">
@ -11,25 +11,54 @@
v-loading="uploadingImg"> v-loading="uploadingImg">
<i class="el-icon-upload"></i> <i class="el-icon-upload"></i>
<div class="el-upload__text"> <div class="el-upload__text">
文件拖到此处 图片拖到此处
<em>点击上传</em> <em>点击上传</em>
</div> </div>
</el-upload> </el-upload>
</el-tab-pane> </el-tab-pane>
<el-tab-pane class="github-panel" label="GitHub 图床" name="github"> <el-tab-pane class="github-panel" label="GitHub 图床" name="github">
<el-form class="setting-form" ref="form" :model="formGitHub" label-position="right" label-width="120px"> <el-form class="setting-form" ref="form" :model="formGitHub" label-position="right" label-width="140px">
<el-form-item label="GitHub 仓库" :required="true"> <el-form-item label="GitHub 仓库" :required="true">
<el-input v-model="formGitHub.repo" placeholder="如github.com/yanglbme/resource"></el-input> <el-input v-model="formGitHub.repo" placeholder="如github.com/yanglbme/resource"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="token" :required="true"> <el-form-item label="token" :required="true">
<el-input v-model="formGitHub.accessToken" <el-input v-model="formGitHub.accessToken" show-password
placeholder="如cc1d0c1426d0fd0902bd2d7184b14da61b8abc46"></el-input> placeholder="如cc1d0c1426d0fd0902bd2d7184b14da61b8abc46"></el-input>
<el-link type="primary" <el-link type="primary"
href="https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token" href="https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token"
target="_blank">如何获取 GitHub token</el-link> target="_blank">如何获取 GitHub token</el-link>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="onSubmit">保存配置</el-button> <el-button type="primary" @click="saveGitHubConfiguration">保存配置</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane class="github-panel" label="阿里云 OSS" name="aliOSS">
<el-form class="setting-form" ref="form" :model="formAliOSS" label-position="right" label-width="140px">
<el-form-item label="AccessKey ID" :required="true">
<el-input v-model="formAliOSS.accessKeyId" placeholder="如LTAI4GdoocsmdoxUf13ylbaNHk"></el-input>
</el-form-item>
<el-form-item label="AccessKey Secret" :required="true">
<el-input v-model="formAliOSS.accessKeySecret" show-password
placeholder="如cc1d0c142doocs0902bd2d7md4b14da6ylbabc46"></el-input>
</el-form-item>
<el-form-item label="Bucket" :required="true">
<el-input v-model="formAliOSS.bucket"
placeholder="如doocs"></el-input>
</el-form-item>
<el-form-item label="Bucket 所在区域" :required="true">
<el-input v-model="formAliOSS.region"
placeholder="如oss-cn-shenzhen"></el-input>
</el-form-item>
<el-form-item label="存储路径">
<el-input v-model="formAliOSS.path"
placeholder="如img"></el-input>
<el-link type="primary"
href="https://help.aliyun.com/document_detail/31883.html"
target="_blank">如何使用阿里云 OSS</el-link>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveAliOSSConfiguration">保存配置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-tab-pane> </el-tab-pane>
@ -55,6 +84,13 @@ export default {
repo: "", repo: "",
accessToken: "", accessToken: "",
}, },
formAliOSS: {
accessKeyId: "",
accessKeySecret: "",
bucket: "",
region: "",
path: ""
},
options: [{ options: [{
value: "default", value: "default",
label: "默认图床", label: "默认图床",
@ -63,6 +99,10 @@ export default {
value: "github", value: "github",
label: "GitHub", label: "GitHub",
}, },
{
value: "aliOSS",
label: "阿里云"
}
], ],
imgHost: "default", imgHost: "default",
uploadingImg: false, uploadingImg: false,
@ -72,16 +112,23 @@ export default {
if (localStorage.getItem("githubConfig")) { if (localStorage.getItem("githubConfig")) {
this.formGitHub = JSON.parse(localStorage.getItem("githubConfig")); this.formGitHub = JSON.parse(localStorage.getItem("githubConfig"));
} }
if (localStorage.getItem("ImgHost")) { if (localStorage.getItem("aliOSSConfig")) {
this.imgHost = localStorage.getItem("ImgHost"); this.formAliOSS = JSON.parse(localStorage.getItem("aliOSSConfig"));
}
if (localStorage.getItem("imgHost")) {
this.imgHost = localStorage.getItem("imgHost");
} }
}, },
methods: { methods: {
changeImgHost() { changeImgHost() {
console.log("select img host:", this.imgHost); localStorage.setItem("imgHost", this.imgHost);
localStorage.setItem("ImgHost", this.imgHost); this.$message({
showClose: true,
message: '已成功切换图床',
type: "success",
});
}, },
onSubmit() { saveGitHubConfiguration() {
if (!(this.formGitHub.repo && this.formGitHub.accessToken)) { if (!(this.formGitHub.repo && this.formGitHub.accessToken)) {
const blankElement = this.formGitHub.repo ? "token" : "GitHub 仓库" const blankElement = this.formGitHub.repo ? "token" : "GitHub 仓库"
this.$message({ this.$message({
@ -92,12 +139,28 @@ export default {
return; return;
} }
localStorage.setItem("githubConfig", JSON.stringify(this.formGitHub)); localStorage.setItem("githubConfig", JSON.stringify(this.formGitHub));
console.log("submit github params:", this.formGitHub);
this.$message({ this.$message({
message: "保存成功", message: "保存成功",
type: "success", type: "success",
}); });
}, },
saveAliOSSConfiguration() {
if (!(this.formAliOSS.accessKeyId && this.formAliOSS.accessKeySecret && this.formAliOSS.bucket && this.formAliOSS.region)) {
this.$message({
showClose: true,
message: `阿里云 OSS 参数配置不全`,
type: "error",
});
return;
}
localStorage.setItem("aliOSSConfig", JSON.stringify(this.formAliOSS));
this.$message({
message: "保存成功",
type: "success",
});
},
// //
beforeUpload(file) { beforeUpload(file) {
if (!this.validateConfig()) { if (!this.validateConfig()) {
@ -121,14 +184,24 @@ export default {
return false; return false;
}, },
validateConfig() { validateConfig() {
switch (this.imgHost) { switch (localStorage.getItem('imgHost')) {
case "github": case "github":
const { const {
repo, accessToken repo, accessToken
} = this.formGitHub; } = this.formGitHub;
if (!repo || !accessToken) { if (!repo || !accessToken) {
this.$message.error("未配置 GitHub 参数"); this.$message.error("请先配置 GitHub 图床参数");
return false;
}
break;
case 'aliOSS':
const {
accessKeyId, accessKeySecret, bucket, region, path
} = this.formAliOSS;
if (!(accessKeyId && accessKeySecret && bucket && region)) {
this.$message.error("请先配置阿里云 OSS 参数");
return false; return false;
} }
} }

View File

@ -1,59 +0,0 @@
const state = {
imgHost: localStorage.getItem("ImgHost") || 'default', // 默认github
default: {
username: 'filess',
repo: 'images',
method: 'put',
accessToken: [
'7715d7ca67b5d3837cfdoocsmde8c38421815aa423510af',
'c411415bf95dbe39625doocsmd5047ba9b7a2a6c9642abe',
'2821cd8819fa345c053doocsmdca86ac653f8bc20db1f1b',
'445f0dae46ef1f2a4d6doocsmdc797301e94797b4750a4c',
'cc1d0c1426d0fd0902bdoocsmdd2d7184b14da61b86ec46',
'b67e9d15cb6f910492fdoocsmdac6b44d379c953bb19eff',
'618c4dc2244ccbbc088doocsmd125d17fd31b7d06a50cf3',
'a4b581732e1c1507458doocsmdc5b223b27dae5e2e16a55'
]
},
qiniuCloud: {}
};
const getters = {
config(state) {
let token, username, repo, method;
const activeKey = state.imgHost !== 'default' && localStorage.getItem(`${state.imgHost}Config`) ? state.imgHost : 'default';
switch (activeKey) {
case 'github':
const githubConfg = JSON.parse(localStorage.getItem("githubConfig"));
const repoUrl = githubConfg.repo.replace("https://github.com/", "").replace("http://github.com/", "").replace("github.com/", "").split("/");
token = githubConfg.accessToken;
username = repoUrl[0];
repo = repoUrl[1];
method = 'put';
break;
case 'qiniuCloud':
break;
default:
token = state.default.accessToken[Math.floor(Math.random() * state.default.accessToken.length)].replace('doocsmd', '');
username = state.default.username;
repo = state.default.repo;
method = state.default.method;
break;
}
const date = new Date();
const dir = date.getFullYear() + '/' + (date.getMonth() + 1).toString().padStart(2, '0') + '/' + date.getDate().toString().padStart(2, '0');
const url = `https://api.github.com/repos/${username}/${repo}/contents/${dir}/`;
return {
method,
token,
url
}
}
};
export default {
namespaced: true,
state,
getters
}

View File

@ -10,8 +10,6 @@ import {
setColor, setColor,
formatDoc formatDoc
} from '../assets/scripts/util' } from '../assets/scripts/util'
// module
import imageApi from './imageApi.js';
Vue.use(Vuex) Vue.use(Vuex)
@ -156,8 +154,5 @@ const mutations = {
export default new Vuex.Store({ export default new Vuex.Store({
state, state,
mutations, mutations,
actions: {}, actions: {}
modules: {
imageApi
}
}) })

View File

@ -135,6 +135,18 @@ export default {
for (let i = 0, len = e.clipboardData.items.length; i < len; ++i) { for (let i = 0, len = e.clipboardData.items.length; i < len; ++i) {
let item = e.clipboardData.items[i] let item = e.clipboardData.items[i]
if (item.kind === 'file') { if (item.kind === 'file') {
//
const imgHost = localStorage.getItem('imgHost') || 'default';
if (imgHost != 'default' && !localStorage.getItem(`${imgHost}Config`)) {
this.$message({
showClose: true,
message: '请先配置好图床参数',
type: 'error'
});
continue;
}
this.isImgLoading = true; this.isImgLoading = true;
const pasteFile = item.getAsFile() const pasteFile = item.getAsFile()
uploadImgFile(pasteFile).then(res => { uploadImgFile(pasteFile).then(res => {
@ -205,7 +217,7 @@ export default {
this.editor.replaceSelection(`\n${markdownImage}\n`, cursor); this.editor.replaceSelection(`\n${markdownImage}\n`, cursor);
this.$message({ this.$message({
showClose: true, showClose: true,
message: '图片插入成功', message: '图片上传成功',
type: 'success' type: 'success'
}); });
this.onEditorRefresh(); this.onEditorRefresh();