From 2ecb33ccb0ee828dc28f72b7828ab5c395b5f0a5 Mon Sep 17 00:00:00 2001 From: Molunerfinn Date: Fri, 22 Dec 2017 22:30:16 +0800 Subject: [PATCH] Added: upyun support --- package.json | 1 + src/main/index.js | 99 ++++++++----- src/main/utils/upYunUpload.js | 80 +++++++++++ src/main/utils/uploader.js | 31 +++- src/renderer/components/SettingPage.vue | 6 +- src/renderer/components/SettingView/Qiniu.vue | 2 +- src/renderer/components/SettingView/UpYun.vue | 132 ++++++++++++++++++ src/renderer/router/index.js | 5 + yarn.lock | 18 ++- 9 files changed, 329 insertions(+), 45 deletions(-) create mode 100644 src/main/utils/upYunUpload.js create mode 100644 src/renderer/components/SettingView/UpYun.vue diff --git a/package.json b/package.json index 5a52c0a0..7325400e 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/main/index.js b/src/main/index.js index b4f7e492..482e768d 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -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', () => { diff --git a/src/main/utils/upYunUpload.js b/src/main/utils/upYunUpload.js new file mode 100644 index 00000000..9dc76dce --- /dev/null +++ b/src/main/utils/upYunUpload.js @@ -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 diff --git a/src/main/utils/uploader.js b/src/main/utils/uploader.js index ba33d9c5..1a26fa01 100644 --- a/src/main/utils/uploader.js +++ b/src/main/utils/uploader.js @@ -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 } } diff --git a/src/renderer/components/SettingPage.vue b/src/renderer/components/SettingPage.vue index be61b3fd..8c5baabc 100644 --- a/src/renderer/components/SettingPage.vue +++ b/src/renderer/components/SettingPage.vue @@ -24,12 +24,16 @@ - 七牛设置 + 七牛云设置 腾讯COS设置 + + + 又拍云设置 + diff --git a/src/renderer/components/SettingView/Qiniu.vue b/src/renderer/components/SettingView/Qiniu.vue index e95d4026..cca0348d 100644 --- a/src/renderer/components/SettingView/Qiniu.vue +++ b/src/renderer/components/SettingView/Qiniu.vue @@ -36,7 +36,7 @@ + + +
+ 又拍云设置 +
+ + + + + + + + + + + + + + + + + + 确定 + + +
+
+ + + + \ No newline at end of file diff --git a/src/renderer/router/index.js b/src/renderer/router/index.js index e88b6526..90fac1ba 100644 --- a/src/renderer/router/index.js +++ b/src/renderer/router/index.js @@ -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, diff --git a/yarn.lock b/yarn.lock index 46895b47..19fa97bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"