├── .eslintrc ├── .gitignore ├── .npmignore ├── server.conf ├── .travis.yml ├── package.json ├── License ├── README.md └── src └── index.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | .DS_Store 4 | yarn-error.log 5 | temp.js 6 | package-lock.json 7 | logs/ 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | yarn-error.log 3 | temp.js 4 | package-lock.json 5 | tsconfig.json 6 | tslint.json 7 | .vscode/ 8 | src/ 9 | .travis.yml 10 | server.conf 11 | -------------------------------------------------------------------------------- /server.conf: -------------------------------------------------------------------------------- 1 | server { 2 | server_name pic-upload.yuki.xin; 3 | 4 | root /app/PicUploader; 5 | 6 | location / { 7 | index index.php; 8 | try_files $uri $uri/ index.php$is_args$args; 9 | } 10 | 11 | location ~ \.php$ { 12 | fastcgi_pass 127.0.0.1:9000; 13 | fastcgi_index index.php; 14 | include fastcgi.conf; 15 | } 16 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: stable 3 | 4 | # Travis-CI Caching 5 | cache: 6 | directories: 7 | - node_modules 8 | yarn: true 9 | 10 | # S: Build Lifecycle 11 | install: 12 | - yarn 13 | 14 | stages: 15 | - name: deploy 16 | 17 | jobs: 18 | include: 19 | - stage: deploy 20 | script: 21 | - npm run build 22 | deploy: 23 | provider: npm 24 | email: "" 25 | api_key: "${NPM_TOKEN}" 26 | skip_cleanup: true 27 | on: 28 | branch: master 29 | branches: 30 | only: 31 | - master 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "picgo-plugin-web-uploader", 3 | "version": "1.1.0", 4 | "description": "自定义Web图床", 5 | "main": "src/index.js", 6 | "publishConfig": { 7 | "access": "public" 8 | }, 9 | "homepage": "", 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1", 12 | "patch": "npm version patch && git push origin master && git push origin --tags", 13 | "minor": "npm version minor && git push origin master && git push origin --tags", 14 | "major": "npm version major && git push origin master && git push origin --tags" 15 | }, 16 | "keywords": [ 17 | "picgo", 18 | "picgo-gui-plugin", 19 | "picgo-plugin" 20 | ], 21 | "author": "ZQian", 22 | "license": "MIT", 23 | "devDependencies": { 24 | "eslint": "^5.0.1", 25 | "eslint-config-standard": "^11.0.0", 26 | "eslint-plugin-import": "^2.13.0", 27 | "eslint-plugin-node": "^6.0.1", 28 | "eslint-plugin-promise": "^3.8.0", 29 | "eslint-plugin-standard": "^3.1.0", 30 | "@varnxy/logger": "^1.1.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ZQian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # picgo-plugin-web-uploader 2 | 3 | plugin for [PicGo](https://github.com/Molunerfinn/PicGo) 4 | 5 | - 自定义Web图床上传 6 | 7 | ## 使用 8 | 9 | ### 图床配置 10 | 11 | - url: 图床上传API地址 12 | - paramName: POST参数名 13 | - jsonPath: 图片URL所在返回值的JsonPath(eg:data.url) 14 | - customHeader: 自定义请求头 标准JSON(eg: {"key":"value"} 15 | - customBody: 自定义Body 标准JSON(eg: {"key":"value"}) 16 | 17 | ### 服务端配置 18 | 19 | 1. 参照 [PicUploader](https://github.com/xiebruce/PicUploader) 配置同时上传多个免费图床 20 | 21 | 2. 可使用已打包的pic-upload docker镜像 22 | ``` 23 | version: '3' 24 | services: 25 | pic-uploader: 26 | image: zqiannnn/pic-uploader:1.0 27 | ports: 28 | - 8080:80 29 | volumes: 30 | - ./server.conf:/etc/nginx/conf.d/server.conf 31 | environment: 32 | - STORAGE_TYPE=Qiniu,Netease,Aliyun,Ucloud,Qingcloud 33 | # 10G 10G 34 | - QINIU_AK=xx 35 | - QINIU_SK=xx 36 | - QINIU_BUCKET=xx 37 | - QINIU_DOMAIN=http://xx.bkt.clouddn.com 38 | # 50G 20G 39 | - NETEASE_AK=xx 40 | - NETEASE_AS=xx 41 | - NETEASE_BUCKET=xx 42 | - NETEASE_ENDPOINT=xx 43 | - NETEASE_DOMAIN=https://xx.nos-eastchina1.126.net 44 | # 无 45 | - ALIYUN_AK=xx 46 | - ALIYUN_AS=xx 47 | - ALIYUN_BUCKET=xx 48 | - ALIYUN_ENDPOINT=xx 49 | - ALIYUN_DOMAIN=https://xx.aliyuncs.com 50 | # 20G 20G 51 | - UCLOUD_PUBLIC_KEY=xx 52 | - UCLOUD_PRIVATE_KEY=xx 53 | - UCLOUD_PROXY_SUFFIX=xx 54 | - UCLOUD_BUCKET=xx 55 | - UCLOUD_ENDPOINT=ufile.ucloud.com.cn 56 | - UCLOUD_DOMAIN=http://xx.ufileos.com 57 | # 30G 11G 58 | - QINGCLOUD_AK=xx 59 | - QINGCLOUD_SK=xx 60 | - QINGCLOUD_BUCKET=xx 61 | - QINGCLOUD_ZONE=xx 62 | # 最后一个domain可设置为步骤3中Nginx域名 63 | - QINGCLOUD_DOMAIN=https://xx.qingstor.com 64 | 65 | # 自定义返回 66 | - CUSTOM_FORMAT={"url":"{{url}}"} 67 | - WATERMARK=水印字符 68 | ``` 69 | 70 | 3. 使用Nginx代理多个免费图床[nginx](https://www.xiebruce.top/644.html) 71 | 72 | ### 服务端测试 73 | - Plugin配置 74 | ![](https://img.yuki.xin/2019/07/01/f2c7c902b2d02e1ad9bdcb929a83dd0d.png) 75 | 76 | - Rest请求 77 | ![](https://i.loli.net/2019/02/27/5c76458ce03e7.png) 78 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // const logger = require('@varnxy/logger') 2 | // logger.setDirectory('/Users/zhang/Work/WorkSpaces/WebWorkSpace/picgo-plugin-web-uploader/logs') 3 | // let log = logger('plugin') 4 | 5 | module.exports = (ctx) => { 6 | const register = () => { 7 | ctx.helper.uploader.register('web-uploader', { 8 | handle, 9 | name: '自定义Web图床', 10 | config: config 11 | }) 12 | } 13 | const handle = async function (ctx) { 14 | let userConfig = ctx.getConfig('picBed.web-uploader') 15 | if (!userConfig) { 16 | throw new Error('Can\'t find uploader config') 17 | } 18 | const url = userConfig.url 19 | const paramName = userConfig.paramName 20 | const jsonPath = userConfig.jsonPath 21 | const customHeader = userConfig.customHeader 22 | const customBody = userConfig.customBody 23 | try { 24 | let imgList = ctx.output 25 | for (let i in imgList) { 26 | let image = imgList[i].buffer 27 | if (!image && imgList[i].base64Image) { 28 | image = Buffer.from(imgList[i].base64Image, 'base64') 29 | } 30 | const postConfig = postOptions(image, customHeader, customBody, url, paramName, imgList[i].fileName) 31 | let body = await ctx.Request.request(postConfig) 32 | 33 | delete imgList[i].base64Image 34 | delete imgList[i].buffer 35 | if (!jsonPath) { 36 | imgList[i]['imgUrl'] = body 37 | } else { 38 | body = JSON.parse(body) 39 | let imgUrl = body 40 | for (let field of jsonPath.split('.')) { 41 | imgUrl = imgUrl[field] 42 | } 43 | if (imgUrl) { 44 | imgList[i]['imgUrl'] = imgUrl 45 | } else { 46 | ctx.emit('notification', { 47 | title: '返回解析失败', 48 | body: '请检查JsonPath设置' 49 | }) 50 | } 51 | } 52 | } 53 | } catch (err) { 54 | ctx.emit('notification', { 55 | title: '上传失败', 56 | body: JSON.stringify(err) 57 | }) 58 | } 59 | } 60 | 61 | const postOptions = (image, customHeader, customBody, url, paramName, fileName) => { 62 | let headers = { 63 | contentType: 'multipart/form-data', 64 | 'User-Agent': 'PicGo' 65 | } 66 | if (customHeader) { 67 | headers = Object.assign(headers, JSON.parse(customHeader)) 68 | } 69 | let formData = {} 70 | if (customBody) { 71 | formData = Object.assign(formData, JSON.parse(customBody)) 72 | } 73 | const opts = { 74 | method: 'POST', 75 | url: url, 76 | headers: headers, 77 | formData: formData 78 | } 79 | opts.formData[paramName] = {} 80 | opts.formData[paramName].value = image 81 | opts.formData[paramName].options = { 82 | filename: fileName 83 | } 84 | return opts 85 | } 86 | 87 | const config = ctx => { 88 | let userConfig = ctx.getConfig('picBed.web-uploader') 89 | if (!userConfig) { 90 | userConfig = {} 91 | } 92 | return [ 93 | { 94 | name: 'url', 95 | type: 'input', 96 | default: userConfig.url, 97 | required: true, 98 | message: 'API地址', 99 | alias: 'API地址' 100 | }, 101 | { 102 | name: 'paramName', 103 | type: 'input', 104 | default: userConfig.paramName, 105 | required: true, 106 | message: 'POST参数名', 107 | alias: 'POST参数名' 108 | }, 109 | { 110 | name: 'jsonPath', 111 | type: 'input', 112 | default: userConfig.jsonPath, 113 | required: false, 114 | message: '图片URL JSON路径(eg: data.url)', 115 | alias: 'JSON路径' 116 | }, 117 | { 118 | name: 'customHeader', 119 | type: 'input', 120 | default: userConfig.customHeader, 121 | required: false, 122 | message: '自定义请求头 标准JSON(eg: {"key":"value"})', 123 | alias: '自定义请求头' 124 | }, 125 | { 126 | name: 'customBody', 127 | type: 'input', 128 | default: userConfig.customBody, 129 | required: false, 130 | message: '自定义Body 标准JSON(eg: {"key":"value"})', 131 | alias: '自定义Body' 132 | } 133 | ] 134 | } 135 | return { 136 | uploader: 'web-uploader', 137 | // transformer: 'web-uploader', 138 | // config: config, 139 | register 140 | 141 | } 142 | } 143 | --------------------------------------------------------------------------------