Browse Source

Merge Image tool

zhuzhuyule 7 years ago
parent
commit
8c61be1aae

+ 7 - 0
app/moe-app.js

@@ -34,6 +34,7 @@ const MoeditorWindow = require('./moe-window'),
 let shellServer = false;
 let qiniuServer = false;
 let cosServer = false;
+let smmsServer = false;
 
 class MoeditorApplication {
     constructor() {
@@ -55,6 +56,12 @@ class MoeditorApplication {
         return shellServer;
     }
 
+    getSmmsServer() {
+        if (!smmsServer)
+            smmsServer = new (require('./tool/hexo-smms'))();
+        return smmsServer;
+    }
+
     getQiniuServer() {
         if (!qiniuServer)
             qiniuServer = new (require('./tool/hexo-qiniu'))();

+ 4 - 4
app/moe-l10n.js

@@ -213,7 +213,7 @@ const strings = {
         "Image": "Image",
         "Source Center": "Source Center",
         "Web Type": "Web Type",
-        "WebSM": "WebSM",
+        "WebSM": "SM.MS",
         "QiNiu": "QiNiu",
         "WebCOS": "Tencent",
         "AccessKey": "Access Key",
@@ -403,9 +403,9 @@ const strings = {
         "Delete": "删除",
         "Select All": "全选",
 
-        "UploadToCOS": "上传腾讯云",
-        "UploadToSMMS": "上传SM.MS",
-        "UploadToQiNiu": "上传七牛",
+        "UploadToCOS": "上传 腾讯云",
+        "UploadToSMMS": "上传 SM.MS",
+        "UploadToQiNiu": "上传 七牛",
         "Quick Open":"快速打开",
         "OpenPathPost":"本文所在路径",
         "OpenPathPostSrc":"本文图片路径",

+ 34 - 9
app/tool/hexo-cos.js

@@ -1,3 +1,9 @@
+/*
+*  This file is part of HexoEditor.
+*
+*  Copyright (c) 2018 zhuzhuyule
+*/
+
 let fs = require('fs');
 let path = require('path');
 
@@ -165,6 +171,23 @@ class COSServer {
             }*/
             if (typeof cb === 'function')
                 cb(err || data);
+
+            if (typeof cb === "function") {
+                let result = {id: localFile};
+                if (err){
+                    result.statusCode = err.statusCode;
+                    result.msg = err.error.message;
+                } else{
+                    result.statusCode = data.statusCode;
+                    result.data = {
+                        localname: path.basename(localFile),
+                        storename: path.basename(serverFile),
+                        path: serverFile,
+                        url: config.Protocol + data.Location.replace(/https?:\/\//,''),
+                    }
+                }
+                cb(result)
+            }
         });
     }
 
@@ -190,23 +213,25 @@ class COSServer {
             *  Bucket: "myblog",
             *  ETag: "",
             *  Key: "test.png",
-            *  Location: "",
+            *  Location: "url",
             *  statusCode: 200,
             *  headers: {}
             }*/
-            if (typeof cb === 'function'){
-                let response = {};
+            if (typeof cb === "function") {
+                let result = {id: localFile};
                 if (err){
-                    response.code = 'error';
-                    response.error = err.error.message;
+                    result.statusCode = err.statusCode;
+                    result.msg = err.error.message;
                 } else{
-                    response.code = 'success';
-                    response.data = {
+                    result.statusCode = data.statusCode;
+                    result.data = {
+                        localname: path.basename(localFile),
+                        storename: path.basename(serverFile),
+                        path: data.Key,
                         url: config.Protocol + data.Location,
-                        hash: data.Key
                     }
                 }
-                cb(localFile,response,data);
+                cb(result)
             }
         });
     }

+ 44 - 28
app/tool/hexo-qiniu.js

@@ -1,5 +1,12 @@
+/*
+*  This file is part of HexoEditor.
+*
+*  Copyright (c) 2018 zhuzhuyule
+*/
+
 class qiniuServer {
     constructor(acessKey, secretKey) {
+        this.XMLHttpRequest = require('./XMLHttpRequest').XMLHttpRequest;
         this.qiniu = require('qiniu');
         this.bucket = '';
         this.qiniu.conf.ACCESS_KEY = acessKey;
@@ -20,8 +27,8 @@ class qiniuServer {
         this.qiniu.conf.ACCESS_KEY = acessKey;
         this.qiniu.conf.SECRET_KEY = secretKey;
         this.mac = new this.qiniu.auth.digest.Mac(acessKey, secretKey);
-        this.bucket = bucket||this.bucket||'';
-        this.url = url||this.url||'';
+        this.bucket = bucket || this.bucket || '';
+        this.url = url || this.url || '';
     }
 
     /**
@@ -52,8 +59,7 @@ class qiniuServer {
      */
     getBuckets(callback) {
         const url_api_bukets = 'https://rs.qbox.me/buckets';
-        let XMLHttpRequest = require('./XMLHttpRequest').XMLHttpRequest;
-        let xhr = new XMLHttpRequest();
+        let xhr = new this.XMLHttpRequest();
         xhr.open('get', url_api_bukets);
         xhr.setRequestHeader('Authorization', this.getAccessToken(url_api_bukets));
         xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
@@ -61,7 +67,7 @@ class qiniuServer {
             if (xhr.readyState === 4) { // 成功完成
                 if (typeof callback === "function") {
                     callback({
-                        code: xhr.status,
+                        statusCode: xhr.status,
                         data: JSON.parse(xhr.responseText)
                     })
                 }
@@ -74,10 +80,9 @@ class qiniuServer {
      * 异步获取空间地址URL列表
      * @param buketName     空间名(必传)
      */
-    getBucketsUrl(buketName,callback) {
+    getBucketsUrl(buketName, callback) {
         const url_api_bukets = 'https://api.qiniu.com/v6/domain/list?tbl=' + buketName;
-        let XMLHttpRequest = require('./XMLHttpRequest').XMLHttpRequest;
-        let xhr = new XMLHttpRequest();
+        let xhr = new this.XMLHttpRequest();
         xhr.open('get', url_api_bukets);
         xhr.setRequestHeader('Authorization', this.getAccessToken(url_api_bukets));
         xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
@@ -85,7 +90,7 @@ class qiniuServer {
             if (xhr.readyState === 4) { // 成功完成
                 if (typeof callback === "function") {
                     callback({
-                        code: xhr.status,
+                        statusCode: xhr.status,
                         data: JSON.parse(xhr.responseText)
                     })
                 }
@@ -103,8 +108,7 @@ class qiniuServer {
         if (!buketName) return;
         const url_api_bukets = require('util').format(
             'https://rsf.qbox.me/list?bucket=%s&marker=&limit=1&prefix=%s&delimiter=/', buketName, prefix || '')
-        let XMLHttpRequest = require('./XMLHttpRequest').XMLHttpRequest;
-        let xhr = new XMLHttpRequest();
+        let xhr = new this.XMLHttpRequest();
         xhr.open('get', url_api_bukets);
         xhr.setRequestHeader('Authorization', this.getAccessToken(url_api_bukets));
         xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
@@ -112,7 +116,7 @@ class qiniuServer {
             if (xhr.readyState === 4) { // 成功完成
                 if (typeof callback === "function") {
                     callback({
-                        code: xhr.status,
+                        statusCode: xhr.status,
                         data: JSON.parse(xhr.responseText)
                     })
                 }
@@ -125,9 +129,21 @@ class qiniuServer {
      * 异步上传单个文件
      * @param localFile         本地文件全路径
      * @param serverFileName    服务器保存名称(可带地址)
-     * @param callback          上传完成响应回调
+     * @param callback  callback(response)    //回调函数
+     *   response = {
+     *      id: 'localFileAbsolutePath',                      //传入文件本地绝对路径
+     *      statusCode: 200|int,                              //服务器代码,200:正常,其他:报错
+     *      data: {
+     *        localname: 'abc.png',                           //本地文件名
+     *        storename: '5a6bea876702d.png',                 //服务器文件名,SM.MS随机生成
+     *        path: '/abc/abc/5a6bea876702d.png',             //服务器路径
+     *        url: 'https://...../abc/abc/5a6bea876702d.png'  //图片地址
+     *      },
+     *      msg: 'error message'                              //一般只有报错才使用到
+     *      errorlist: 'url'                                  //一般只有报错才使用到
+     *   }
      */
-    uploadFile(localFile,serverFileName, callback) {
+    uploadFile(localFile, serverFileName, callback) {
         //生成上传 Token
         let token = this.getUptoken(this.bucket, serverFileName);
         var formUploader = new this.qiniu.form_up.FormUploader(this.qiniu.conf);
@@ -135,24 +151,24 @@ class qiniuServer {
         let qiniuServer = this;
         formUploader.putFile(token, serverFileName, localFile, extra,
             function (respErr, respBody, respInfo) {
-                console.log( respBody);
-                if (respErr) {
-                    throw respErr;
-                }
-                if (respInfo.statusCode == 200 || respInfo.statusCode == 579) {
-                    let response = {
-                        code: 'success',
-                        data: {
+                if (typeof  callback == 'function') {
+                    let result = {id: localFile};
+                    if (respInfo.statusCode == 200 || respInfo.statusCode == 579) {
+                        result.statusCode = 200;
+                        result.data = {
+                            localname: path.basename(localFile),
+                            storename: path.basename(serverFileName),
+                            path: respBody.key,
                             url: qiniuServer.url + respBody.key
                         }
+                        result.msg = '';
+                        result.errorlist = '';
+                    } else {
+                        result.msg = respInfo.statusCode + respBody.error;
+                        result.errorlist = 'https://developer.qiniu.com/kodo/api/3928/error-responses#2';
                     }
-                    callback(localFile, response);
                 } else {
-                    console.log(respInfo.statusCode);
-                    let response = {
-                        error: respInfo.statusCode + respBody,
-                    }
-                    callback(localFile, response);
+                    console.log(respBody)
                 }
             });
     }

+ 2 - 44
app/tool/hexo-shell.js

@@ -1,49 +1,7 @@
 /*
-*  This file is part of Moeditor.
-*
-*  Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
-*  Copyright (c) 2016 lucaschimweg
-*  Copyright (c) 2016 douglaseccker
-*  Copyright (c) 2016 PifyZ
-*  Copyright (c) 2016 Hyuchia
-*  Copyright (c) 2016 welksonramos
-*  Copyright (c) 2016 caiocdasilva
-*  Copyright (c) 2016 lawgsy <lawgsy@gmail.com>
-*
-*  Moeditor is free software: you can redistribute it and/or modify
-*  it under the terms of the GNU General Public License as published by
-*  the Free Software Foundation, either version 3 of the License, or
-*  (at your option) any later version.
-*
-*  Moeditor is distributed in the hope that it will be useful,
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-*  GNU General Public License for more details.
-*
-*  You should have received a copy of the GNU General Public License
-*  along with Moeditor. If not, see <http://www.gnu.org/licenses/>.
-*
-*  The translation providers:
-*   - en: Menci
-*   - zh_CN: Menci
-*   - de: lucaschimweg
-*   - pt: douglaseccker & welksonramos & caiocdasilva
-*   - fr: PifyZ
-*   - es: Hyuchia
-*   - nl: lawgsy
-*   - it: iamsisar
-*
-*  If you want to help translate this app, please copy the `en` items of the
-*  `strings` constant as the template, and fill each item with the translated
-*  string. The `_name` item in a language is the language's name.
-*
-*  You can translate for a language (e.g. en) or a language with specified
-*  region (e.g. zh_CN). The app will choose the language with specified region
-*  first, fallback to only language when the former is unavailable, and fallback
-*  to English when they are all unavailable.
-*
-*  Send a PR to us after translating.
+*  This file is part of HexoEditor.
 *
+*  Copyright (c) 2018 zhuzhuyule
 */
 
 'use strict'

+ 92 - 0
app/tool/hexo-smms.js

@@ -0,0 +1,92 @@
+/*
+*  This file is part of HexoEditor.
+*
+*  Copyright (c) 2018 zhuzhuyule
+*
+*/
+
+const path = require('path');
+const fs = require('fs');
+
+class Smms {
+    constructor() {
+        this.__ = require('lodash');
+        this.FormData = require('form-data');
+    }
+
+    /**
+     * 生成File对象
+     * @param filename
+     * @param serverName
+     * @returns {File}
+     */
+    getFormData(filename,serverName) {
+        let buffer = fs.readFileSync(filename);
+        let type = path.extname(filename)
+        type = (type && type.slice(1)) || 'jpg';
+        let form = new this.FormData();
+        form.append('smfile', buffer,{
+            filename: serverName || path.basename(filename),
+            contentType: 'image/' + type
+        });
+        return form;
+    }
+
+
+    /**
+     * 上传文件
+     * @param localFile                       //本地文件
+     * @param serverFileName                  //服务器文件名称
+     * @param callback  callback(response)    //回调函数
+     *   response = {
+     *      id: 'localFileAbsolutePath',                      //传入文件本地绝对路径
+     *      statusCode: 200|int,                              //服务器代码,200:正常,其他:报错
+     *      data: {
+     *        localname: 'abc.png',                           //本地文件名
+     *        storename: '5a6bea876702d.png',                 //服务器文件名,SM.MS随机生成
+     *        path: '/abc/abc/5a6bea876702d.png',             //服务器路径
+     *        url: 'https://...../abc/abc/5a6bea876702d.png'  //图片地址
+     *      },
+     *      msg: 'error message'                              //一般只有报错才使用到
+     *   }
+     */
+    uploadFile(localFile,serverFileName,callback) {
+        var formData = this.getFormData(localFile,serverFileName);
+        var request = require('request')({
+            url: "https://sm.ms/api/upload",
+            method: 'POST',
+            headers: this.__.extend({}, formData.getHeaders(), {
+                'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36',
+            })
+        },(error,res,body)=>{
+            if (typeof callback === "function") {
+                console.log(body)
+                let response = JSON.parse(body);
+                let result = {id: localFile};
+                if (res.statusCode == 200) {
+                    result.statusCode = (response.code == 'success' ? 200 : -1);
+                    if (result.statusCode == 200) {
+                        result.data = {
+                            localname: response.data.filename,
+                            storename: response.data.storename,
+                            path: response.data.path,
+                            url: response.data.url
+                        }
+                        result.msg = '';
+                    } else {
+                        result.msg = response.msg;
+                    }
+                } else {
+                    result.statusCode = res.statusCode;
+                    result.msg = res.statusMessage;
+                }
+                callback(result)
+            } else {
+                console.log(res)
+            }
+        });
+        formData.pipe(request);
+    }
+}
+
+module.exports = Smms;

+ 2 - 0
package.json

@@ -73,6 +73,7 @@
     "electron-titlebar": "0.0.2",
     "flowchart.js": "^1.6.3",
     "font-awesome": "^4.6.3",
+    "form-data": "^2.3.1",
     "hexo-front-matter": "^0.2.3",
     "hexo-fs": "^0.2.2",
     "hexo-renderer-ejs": "^0.3.1",
@@ -94,6 +95,7 @@
     "os-locale": "^1.4.0",
     "qiniu": "^7.1.1",
     "raphael": "^2.2.1",
+    "request": "^2.83.0",
     "strip-indent": "^2.0.0",
     "swig": "^1.4.2",
     "swig-extras": "0.0.1",

+ 31 - 46
views/main/hexo-image.js

@@ -81,6 +81,12 @@ class ImgManager {
         return path.resolve(this.imgBaseDir, p).replace(/\\/g, '/');
     }
 
+    getSmmsServer() {
+        if (!this.smmsServer) {
+            this.smmsServer = moeApp.getSmmsServer();
+        }
+        return this.smmsServer;
+    }
     getQiNiuServer() {
         if (!this.qiniuServer) {
             // this.qiniuServer = new (require('./hexo-qiniu'))();
@@ -224,31 +230,8 @@ class ImgManager {
         xhr.send();
     }
 
-    asyncUploadToSm(imgPath, file, callback) {
-        let formdata = new FormData();
-        formdata.append('smfile', file);
-        let xhr = new XMLHttpRequest();
-        xhr.open('post', 'https://sm.ms/api/upload');
-        xhr.onreadystatechange = () => {
-            if (xhr.readyState === 4) { // 成功完成
-                // 判断响应结果:
-                if (xhr.status === 200) {
-                    // 成功,通过responseText拿到响应的文本:
-                    if (typeof callback === "function") {
-                        callback(imgPath, JSON.parse(xhr.responseText))
-                    }
-                } else {
-                    // 失败,根据响应码判断失败原因:
-                    if (typeof callback === "function") {
-                        callback(imgPath, {code: xhr.status})
-                    }
-                }
-            }
-        }
-        // xhr.upload.onprogress = (e) => {
-        //     console.log(Math.floor(100 * e.loaded / e.total) + '%');
-        // }
-        xhr.send(formdata)
+    asyncUploadToSmms(imgPath, callback) {
+        this.getSmmsServer().uploadFile(imgPath,'',callback);
     }
 
     asyncUploadToQiNiu(imgPath, callback) {
@@ -268,8 +251,7 @@ class ImgManager {
                 this.asyncUploadToCOS(imgPath,callback)
                 break;
             default:
-                let file = new File([fs.readFileSync(imgPath)], md5(imgPath) + path.extname(imgPath), {type: 'image/' + path.extname(imgPath).slice(1)});
-                this.asyncUploadToSm(imgPath,file, callback);
+                this.asyncUploadToSmms(imgPath, callback);
         }
     }
 
@@ -313,24 +295,27 @@ class ImgManager {
          * 回调函数
          * @param fileID
          * @param response
-         *
-         * response={
-         *    code: 'success'|'error'
-         *    data: {
-         *        url: 'http....',
-         *        hash: '.....'
-         *          }
-         *    error: 'error'
+         *   response = {
+         *      id: 'localFileAbsolutePath',                      //传入文件本地绝对路径
+         *      statusCode: 200|int,                              //服务器代码,200:正常,其他:报错
+         *      data: {
+         *        localname: 'abc.png',                           //本地文件名
+         *        storename: '5a6bea876702d.png',                 //服务器文件名,SM.MS随机生成
+         *        path: '/abc/abc/5a6bea876702d.png',             //服务器路径
+         *        url: 'https://...../abc/abc/5a6bea876702d.png'  //图片地址
+         *      },
+         *      msg: 'error message'                              //一般只有报错才使用到
+         *      errorlist: 'url'                                  //一般只有报错才使用到
+         *   }
          * }
          */
-        function uploadRequest(fileID, response,info) {
+        function uploadRequest(response) {
             finishedCount++;
             console.log(finishedCount + '/' + uploadList.size)
-            if (info) console.log(info)
-            if (response.code == 'success') {
-                successList.set(fileID, response)
+            if (response.statusCode == 200) {
+                successList.set(response.id, response)
             } else {
-                errorList.set(fileID, response.error)
+                errorList.set(response.id, response)
             }
             if (finishedCount >= uploadList.size) {
                 checktime(true);
@@ -342,10 +327,10 @@ class ImgManager {
 
         function updateSrc() {
             let value = editor.getValue();
-            successList.forEach((v, k) => {
-                value = value.replace(new RegExp(imgManager.relativePath(k), 'g'), v.data.url);
-                imgManager.imgPathToUrl[k] = v.data.url;
-                imgManager.imgPathToDel[k] = v.data.hash;
+            successList.forEach((response) => {
+                value = value.replace(new RegExp(imgManager.relativePath(response.id), 'g'), response.data.url);
+                imgManager.imgPathToUrl[response.id] = response.data.url;
+                imgManager.imgPathToDel[response.id] = response.data.hash;
             })
 
             editor.setValue(value);
@@ -357,8 +342,8 @@ class ImgManager {
             try {
                 updateSrc();
                 let errMsg = '';
-                errorList.forEach((v, k) => {
-                    errMsg += k + ':' + __(v) + '</br>';
+                errorList.forEach((response) => {
+                    errMsg += response.id + ':' + __(response.msg) + '</br>';
                 })
                 if (isTimeout) errMsg += 'Upload time out.';
                 if (errMsg) {

+ 5 - 4
views/settings/moe-settings.js

@@ -482,7 +482,7 @@ document.addEventListener('DOMContentLoaded', () => {
     let imageTabContents = document.querySelector('.panel[data-tab="image"]');
     let imageType = imageTabContents.querySelector('#image-web-type');
 
-       //QiNiu
+    //QiNiu
     let imageAccessKey = imageTabContents.querySelector('#image-qiniu-accessKey');
     let imageSecretKey = imageTabContents.querySelector('#image-qiniu-secretKey');
     let imageBucket = imageTabContents.querySelector('#image-qiniu-bucket');
@@ -532,6 +532,7 @@ document.addEventListener('DOMContentLoaded', () => {
         imageTabItem.click();
     })
 
+    //QiNiu
     function hasQiNiuServer() {
         if (imageAccessKey.value && imageSecretKey.value && !global.qiniuServer) {
             global.qiniuServer = moeApp.getQiniuServer();
@@ -557,7 +558,7 @@ document.addEventListener('DOMContentLoaded', () => {
             let oldBucket = moeApp.config.get(imageBucket.id);
             qiniuServer.getBuckets((response) => {
                 imageBucket.innerHTML = '';
-                if (response.code == 200) {
+                if (response.statusCode == 200) {
                     response.data.forEach((name) => {
                         let option = document.createElement('option');
                         option.value = name;
@@ -583,7 +584,7 @@ document.addEventListener('DOMContentLoaded', () => {
         if (hasQiNiuServer()) {
             let oldurl = moeApp.config.get(imageBaseWeb.id);
             qiniuServer.getBucketsUrl(bucket, (response) => {
-                if (response.code == 200) {
+                if (response.statusCode == 200) {
                     imageBaseWeb.innerHTML = '';
                     response.data.forEach((url) => {
                         let option = document.createElement('option');
@@ -682,7 +683,7 @@ document.addEventListener('DOMContentLoaded', () => {
     })
 
 
-    //腾讯
+    //Tencent
     function hasCosServer() {
         if (imageCosAccessKey.value && imageCosSecretKey.value && !global.cosServer) {
             global.cosServer = moeApp.getCOSServer();