Added: upyun support

This commit is contained in:
Molunerfinn
2017-12-22 22:30:16 +08:00
parent d1602edf65
commit 2ecb33ccb0
9 changed files with 329 additions and 45 deletions

View File

@@ -71,6 +71,7 @@
"image-size": "^0.6.1",
"lodash-id": "^0.14.0",
"lowdb": "^1.0.0",
"md5": "^2.2.1",
"melody.css": "^1.0.2",
"qiniu": "^7.1.1",
"request": "^2.83.0",

View File

@@ -23,6 +23,14 @@ const settingWinURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/#setting/upload`
: `file://${__dirname}/index.html#setting/upload`
const uploadFailed = () => {
const notification = new Notification({
title: '上传失败',
body: '请检查你的图床配置!'
})
notification.show()
}
function createTray () {
tray = new Tray(`${__static}/menubar.png`)
const contextMenu = Menu.buildFromTemplate([
@@ -68,6 +76,15 @@ function createTray () {
db.read().set('picBed.current', 'tcyun')
.write()
}
},
{
label: '又拍云图床',
type: 'radio',
checked: db.read().get('picBed.current').value() === 'upyun',
click () {
db.read().set('picBed.current', 'upyun')
.write()
}
}
]
},
@@ -109,18 +126,22 @@ function createTray () {
tray.on('drop-files', async (event, files) => {
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
const imgs = await uploader(files, 'imgFromPath', window.webContents)
for (let i in imgs) {
clipboard.writeText(pasteTemplate(pasteStyle, imgs[i].imgUrl))
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl,
icon: files[i]
})
setTimeout(() => {
notification.show()
}, i * 100)
if (imgs !== false) {
for (let i in imgs) {
clipboard.writeText(pasteTemplate(pasteStyle, imgs[i].imgUrl))
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl,
icon: files[i]
})
setTimeout(() => {
notification.show()
}, i * 100)
}
window.webContents.send('dragFiles', imgs)
} else {
uploadFailed()
}
window.webContents.send('dragFiles', imgs)
})
// toggleWindow()
}
@@ -232,35 +253,43 @@ const showWindow = () => {
ipcMain.on('uploadClipboardFiles', async (evt, file) => {
const img = await uploader(file, 'imgFromClipboard', window.webContents)
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
clipboard.writeText(pasteTemplate(pasteStyle, img[0].imgUrl))
const notification = new Notification({
title: '上传成功',
body: img[0].imgUrl,
icon: file[0]
})
notification.show()
window.webContents.send('clipboardFiles', [])
window.webContents.send('uploadFiles', img)
if (img !== false) {
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
clipboard.writeText(pasteTemplate(pasteStyle, img[0].imgUrl))
const notification = new Notification({
title: '上传成功',
body: img[0].imgUrl,
icon: file[0]
})
notification.show()
window.webContents.send('clipboardFiles', [])
window.webContents.send('uploadFiles', img)
} else {
uploadFailed()
}
})
ipcMain.on('uploadChoosedFiles', async (evt, files) => {
const imgs = await uploader(files, 'imgFromUploader', settingWindow.webContents)
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
let pasteText = ''
for (let i in imgs) {
pasteText += pasteTemplate(pasteStyle, imgs[i].imgUrl) + '\r\n'
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl,
icon: files[i].path
})
setTimeout(() => {
notification.show()
}, i * 100)
if (imgs !== false) {
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
let pasteText = ''
for (let i in imgs) {
pasteText += pasteTemplate(pasteStyle, imgs[i].imgUrl) + '\r\n'
const notification = new Notification({
title: '上传成功',
body: imgs[i].imgUrl,
icon: files[i].path
})
setTimeout(() => {
notification.show()
}, i * 100)
}
clipboard.writeText(pasteText)
window.webContents.send('uploadFiles', imgs)
} else {
uploadFailed()
}
clipboard.writeText(pasteText)
window.webContents.send('uploadFiles', imgs)
})
app.on('ready', () => {

View File

@@ -0,0 +1,80 @@
import request from 'request-promise'
import * as img2Base64 from './img2base64'
import db from '../../datastore/index'
import { Notification, clipboard } from 'electron'
import crypto from 'crypto'
import MD5 from 'md5'
// generate COS signature string
const generateSignature = (fileName) => {
const options = db.read().get('picBed.upyun').value()
// const apiKey = options.apiKey
const operator = options.operator
const password = options.password
const md5Password = MD5(password)
const date = new Date().toGMTString()
const uri = `/${options.bucket}/${fileName}`
const value = `PUT&${uri}&${date}`
const sign = crypto.createHmac('sha1', md5Password).update(value).digest('base64')
return `UPYUN ${operator}:${sign}`
}
const postOptions = (fileName, signature, imgBase64) => {
const options = db.read().get('picBed.upyun').value()
const bucket = options.bucket
return {
method: 'PUT',
url: `https://v0.api.upyun.com/${bucket}/${fileName}`,
headers: {
Authorization: signature,
Date: new Date().toGMTString()
},
body: Buffer.from(imgBase64, 'base64'),
resolveWithFullResponse: true
}
}
const upYunUpload = async (img, type, webContents) => {
try {
webContents.send('uploadProgress', 0)
const imgList = await img2Base64[type](img)
webContents.send('uploadProgress', 30)
const length = imgList.length
const upyunOptions = db.read().get('picBed.upyun').value()
for (let i in imgList) {
const singature = generateSignature(imgList[i].fileName)
const options = postOptions(imgList[i].fileName, singature, imgList[i].base64Image)
const body = await request(options)
if (body.statusCode === 200) {
delete imgList[i].base64Image
imgList[i]['imgUrl'] = `${upyunOptions.url}/${imgList[i].fileName}${upyunOptions.options}`
imgList[i]['type'] = 'upyun'
if (i - length === -1) {
webContents.send('uploadProgress', 60)
}
} else {
webContents.send('uploadProgress', -1)
const notification = new Notification({
title: '上传失败!',
body: '服务端出错,请稍后再试'
})
notification.show()
}
}
webContents.send('uploadProgress', 100)
return imgList
} catch (err) {
console.log(err)
const body = JSON.parse(err.error)
webContents.send('uploadProgress', -1)
const notification = new Notification({
title: '上传失败!',
body: `错误码:${body.code},请打开浏览器粘贴地址查看相关原因。`
})
notification.show()
clipboard.writeText('http://docs.upyun.com/api/errno/')
// throw new Error(err)
}
}
export default upYunUpload

View File

@@ -1,16 +1,33 @@
import weiboUpload from './weiboUpload'
import qiniuUpload from './qiniuUpload'
import tcYunUpload from './tcYunUpload'
import upYunUpload from './upYunUpload'
import db from '../../datastore/index'
const checkUploader = (type) => {
const currentUploader = db.read().get(`picBed.${type}`).value()
if (currentUploader) {
return true
} else {
return false
}
}
const uploader = (img, type, webContents) => {
const uploadType = db.read().get('picBed.current').value()
switch (uploadType) {
case 'weibo':
return weiboUpload(img, type, webContents)
case 'qiniu':
return qiniuUpload(img, type, webContents)
case 'tcyun':
return tcYunUpload(img, type, webContents)
if (checkUploader(uploadType)) {
switch (uploadType) {
case 'weibo':
return weiboUpload(img, type, webContents)
case 'qiniu':
return qiniuUpload(img, type, webContents)
case 'tcyun':
return tcYunUpload(img, type, webContents)
case 'upyun':
return upYunUpload(img, type, webContents)
}
} else {
return false
}
}

View File

@@ -24,12 +24,16 @@
</el-menu-item>
<el-menu-item index="qiniu">
<i class="el-icon-ui-qiniu"></i>
<span slot="title">七牛设置</span>
<span slot="title">七牛设置</span>
</el-menu-item>
<el-menu-item index="tcyun">
<i class="el-icon-ui-tcyun"></i>
<span slot="title">腾讯COS设置</span>
</el-menu-item>
<el-menu-item index="upyun">
<i class="el-icon-ui-upyun"></i>
<span slot="title">又拍云设置</span>
</el-menu-item>
</el-menu>
</el-col>
<el-col :span="19" :offset="5">

View File

@@ -36,7 +36,7 @@
<el-input v-model="form.bucket" @keyup.native.enter="confirm" placeholder="Bucket"></el-input>
</el-form-item>
<el-form-item
label="设定上传网址"
label="设定访问网址"
prop="url"
:rules="{
required: true, message: '网址不能为空', trigger: 'blur'

View File

@@ -0,0 +1,132 @@
<template>
<div id="tcyun-view">
<el-row :gutter="16">
<el-col :span="16" :offset="4">
<div class="view-title">
又拍云设置
</div>
<el-form
ref="tcyun"
label-position="right"
label-width="120px"
:model="form"
size="mini">
<el-form-item
label="设定存储空间名"
prop="bucket"
:rules="{
required: true, message: 'Bucket不能为空', trigger: 'blur'
}">
<el-input v-model="form.bucket" @keyup.native.enter="confirm" placeholder="Bucket"></el-input>
</el-form-item>
<el-form-item
label="设定操作员"
prop="operator"
:rules="{
required: true, message: '操作员不能为空', trigger: 'blur'
}">
<el-input v-model="form.operator" @keyup.native.enter="confirm" placeholder="例如me"></el-input>
</el-form-item>
<el-form-item
label="设定操作员密码"
prop="password"
:rules="{
required: true, message: '操作员密码不能为空', trigger: 'blur'
}">
<el-input v-model="form.password" @keyup.native.enter="confirm" placeholder="输入操作员密码" type="password"></el-input>
</el-form-item>
<el-form-item
label="设定加速域名"
prop="url"
:rules="{
required: true, message: '加速域名不能为空', trigger: 'blur'
}">
<el-input v-model="form.url" placeholder="例如http://xxx.test.upcdn.net" @keyup.native.enter="confirm()"></el-input>
</el-form-item>
<el-form-item
label="设定网址后缀"
>
<el-input v-model="form.options" @keyup.native.enter="confirm" placeholder="例如!imgslim"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="confirm">确定</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import mixin from '../mixin'
export default {
name: 'upyun',
mixins: [mixin],
data () {
return {
form: {
bucket: '',
operator: '',
password: '',
options: ''
}
}
},
created () {
const config = this.$db.get('picBed.upyun').value()
if (config) {
for (let i in config) {
this.form[i] = config[i]
}
}
},
methods: {
confirm () {
this.$refs.tcyun.validate((valid) => {
if (valid) {
this.$db.set('picBed.upyun', this.form).write()
const successNotification = new window.Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
} else {
return false
}
})
}
}
}
</script>
<style lang='stylus'>
.view-title
color #eee
font-size 20px
text-align center
margin 20px auto
#tcyun-view
.el-form
label
line-height 22px
padding-bottom 0
color #eee
.el-button
width 100%
border-radius 19px
.el-input__inner
border-radius 19px
.el-radio-group
width 100%
label
width 25%
.el-radio-button__inner
width 100%
.el-radio-button:first-child
.el-radio-button__inner
border-left none
border-radius 14px 0 0 14px
.el-radio-button:last-child
.el-radio-button__inner
border-left none
border-radius 0 14px 14px 0
</style>

View File

@@ -35,6 +35,11 @@ export default new Router({
component: require('@/components/SettingView/TcYun').default,
name: 'tcyun'
},
{
path: 'upyun',
component: require('@/components/SettingView/UpYun').default,
name: 'upyun'
},
{
path: 'gallery',
component: require('@/components/SettingView/Gallery').default,

View File

@@ -1861,6 +1861,10 @@ chardet@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.0.tgz#0bbe1355ac44d7a3ed4a925707c4ef70f8190f6c"
charenc@~0.0.1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
check-error@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
@@ -2303,6 +2307,10 @@ cross-unzip@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/cross-unzip/-/cross-unzip-0.0.2.tgz#5183bc47a09559befcf98cc4657964999359372f"
crypt@~0.0.1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
cryptiles@2.x.x:
version "2.0.5"
resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
@@ -4392,7 +4400,7 @@ is-binary-path@^1.0.0:
dependencies:
binary-extensions "^1.0.0"
is-buffer@^1.0.2, is-buffer@^1.1.5:
is-buffer@^1.0.2, is-buffer@^1.1.5, is-buffer@~1.1.1:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
@@ -5269,6 +5277,14 @@ md5.js@^1.3.4:
hash-base "^3.0.0"
inherits "^2.0.1"
md5@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
dependencies:
charenc "~0.0.1"
crypt "~0.0.1"
is-buffer "~1.1.1"
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"