Ver código fonte

Image hosting code refactoring (#1032)

* 将图床单独提取出来成为一个类

* Split the image hosting code into two classes

* change null to parent

* delect empty line,add p_,rename file and so on
冯文华 6 anos atrás
pai
commit
a85c39aa26
6 arquivos alterados com 836 adições e 713 exclusões
  1. 2 2
      src/resources/vnote.ini
  2. 2 0
      src/src.pro
  3. 692 0
      src/vimagehosting.cpp
  4. 131 0
      src/vimagehosting.h
  5. 6 653
      src/vmdtab.cpp
  6. 3 58
      src/vmdtab.h

+ 2 - 2
src/resources/vnote.ini

@@ -1,9 +1,9 @@
 [global]
-; Wechat ImageBed
+; Wechat Image Hosting
 wechat_appid=
 wechat_secret=
 wechat_markdown_to_wechat_tool_url=
-; Github ImageBed
+; Github Image Hosting
 github_personal_access_token=
 github_repos_name=
 github_user_name=

+ 2 - 0
src/src.pro

@@ -28,6 +28,7 @@ TRANSLATIONS += translations/vnote_zh_CN.ts \
 }
 
 SOURCES += main.cpp\
+    vimagehosting.cpp \
     vmainwindow.cpp \
     vdirectorytree.cpp \
     vnote.cpp \
@@ -165,6 +166,7 @@ SOURCES += main.cpp\
 
 HEADERS  += vmainwindow.h \
     vdirectorytree.h \
+    vimagehosting.h \
     vnote.h \
     vnotebook.h \
     dialog/vnewdirdialog.h \

+ 692 - 0
src/vimagehosting.cpp

@@ -0,0 +1,692 @@
+#include "vimagehosting.h"
+#include "utils/vutils.h"
+#include "vedittab.h"
+
+extern VConfigManager *g_config;
+
+VGithubImageHosting::VGithubImageHosting(VFile *p_file, QWidget *p_parent)
+    :QObject(p_parent),
+     m_file(p_file)
+{
+    reply = Q_NULLPTR;
+    imageUploaded = false;
+}
+
+void VGithubImageHosting::handleUploadImageToGithubRequested()
+{
+    qDebug() << "Start processing the image upload request to GitHub";
+
+    if(g_config->getpersonalAccessToken().isEmpty() || g_config->getReposName().isEmpty() || g_config->getUserName().isEmpty())
+    {
+        qDebug() << "Please configure the GitHub image hosting first!";
+        QMessageBox::warning(NULL, tr("Github Image Hosting"), tr("Please configure the GitHub image hosting first!"));
+        return;
+    }
+
+    authenticateGithubImageHosting(g_config->getpersonalAccessToken());
+}
+
+void VGithubImageHosting::authenticateGithubImageHosting(QString p_token)
+{
+    qDebug() << "start the authentication process ";
+    QApplication::setOverrideCursor(Qt::WaitCursor);
+    QNetworkRequest request;
+    QUrl url = QUrl("https://api.github.com");
+    QString ptoken = "token " + p_token;
+    request.setRawHeader("Authorization", ptoken.toLocal8Bit());
+    request.setUrl(url);
+    if(reply != Q_NULLPTR) {
+        reply->deleteLater();
+    }
+    reply = manager.get(request);
+    connect(reply, &QNetworkReply::finished, this, &VGithubImageHosting::githubImageBedAuthFinished);
+}
+
+void VGithubImageHosting::githubImageBedAuthFinished()
+{
+    switch (reply->error()) {
+    case QNetworkReply::NoError:
+    {
+        QByteArray bytes = reply->readAll();
+
+        if(bytes.contains("Bad credentials")){
+            qDebug() << "Authentication failed";
+            QApplication::restoreOverrideCursor();  // Recovery pointer
+            QMessageBox::warning(NULL, tr("Github Image Hosting"), tr("Bad credentials!! ") +
+                                 tr("Please check your Github Image Hosting parameters !!"));
+            return;
+        }else{
+            qDebug() << "Authentication completed";
+
+            qDebug() << "The current article path is: " << m_file->fetchPath();
+            imageBasePath = m_file->fetchBasePath();
+            newFileContent = m_file->getContent();
+
+            QVector<ImageLink> images = VUtils::fetchImagesFromMarkdownFile(m_file,ImageLink::LocalRelativeInternal);
+            QApplication::restoreOverrideCursor();  // Recovery pointer
+            if(images.size() > 0)
+            {
+                proDlg = new QProgressDialog(tr("Uploading images to github..."),
+                                       tr("Abort"),
+                                       0,
+                                       images.size(),
+                                       NULL);
+                proDlg->setWindowModality(Qt::WindowModal);
+                proDlg->setWindowTitle(tr("Uploading Images To Github"));
+                proDlg->setMinimumDuration(1);
+
+                uploadImageCount = images.size();
+                uploadImageCountIndex  = uploadImageCount;
+                for(int i=0;i<images.size() ;i++)
+                {
+                    if(images[i].m_url.contains(".png") || images[i].m_url.contains(".jpg")|| images[i].m_url.contains(".gif")){
+                        imageUrlMap.insert(images[i].m_url,"");
+                    }else{
+                        delete proDlg;
+                        imageUrlMap.clear();
+                        qDebug() << "Unsupported type...";
+                        QFileInfo fileInfo(images[i].m_path.toLocal8Bit());
+                        QString fileSuffix = fileInfo.suffix();
+                        QString info = tr("Unsupported type: ") + fileSuffix;
+                        QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
+                        return;
+                    }
+                }
+                githubImageBedUploadManager();
+            }
+            else
+            {
+                qDebug() << m_file->getName() << " No images to upload";
+                QString info = m_file->getName() + " No pictures to upload";
+                QMessageBox::information(NULL, tr("Github Image Hosting"), info);
+            }
+        }
+        break;
+    }
+    default:
+    {
+        QApplication::restoreOverrideCursor();  // Recovery pointer
+        qDebug() << "Network error: " << reply->errorString() << " error " << reply->error();
+        QString info = tr("Network error: ") + reply->errorString();
+        QMessageBox::warning(NULL, tr("Github Image Hosting"), info);
+    }
+    }
+}
+
+void VGithubImageHosting::githubImageBedUploadManager()
+{
+    uploadImageCountIndex--;
+
+    QString imageToUpload;
+    QMapIterator<QString, QString> it(imageUrlMap);
+    while(it.hasNext())
+    {
+        it.next();
+        if(it.value() == ""){
+            imageToUpload = it.key();
+            proDlg->setValue(uploadImageCount - 1 - uploadImageCountIndex);
+            proDlg->setLabelText(tr("Uploaading image: %1").arg(imageToUpload));
+            break;
+        }
+    }
+
+    if(imageToUpload == ""){
+        qDebug() << "All images have been uploaded";
+        githubImageBedReplaceLink(newFileContent, m_file->fetchPath());
+        return;
+    }
+
+    if(g_config->getpersonalAccessToken().isEmpty() ||
+            g_config->getReposName().isEmpty() ||
+            g_config->getUserName().isEmpty())
+    {
+        qDebug() << "Please configure the GitHub image hosting first!";
+        QMessageBox::warning(NULL, tr("Github Image Hosting"), tr("Please configure the GitHub image hosting first!"));
+        imageUrlMap.clear();
+        return;
+    }
+
+    QString path = imageBasePath + QDir::separator();
+    path += imageToUpload;
+    githubImageBedUploadImage(g_config->getUserName(),
+                              g_config->getReposName(),
+                              path,
+                              g_config->getpersonalAccessToken());
+}
+
+void VGithubImageHosting::githubImageBedUploadImage(const QString &p_username,
+                                                    const QString &p_repository,
+                                                    const QString &p_image_path,
+                                                    const QString &p_token)
+{
+    QFileInfo fileInfo(p_image_path.toLocal8Bit());
+    if(!fileInfo.exists()){
+        qDebug() << "The picture does not exist in this path: " << p_image_path.toLocal8Bit();
+        QString info = tr("The picture does not exist in this path: ") + p_image_path.toLocal8Bit();
+        QMessageBox::warning(NULL, tr("Github Image Hosting"), info);
+        imageUrlMap.clear();
+        if(imageUploaded){
+            githubImageBedReplaceLink(newFileContent, m_file->fetchPath());
+        }
+        return;
+    }
+
+    QString fileSuffix = fileInfo.suffix();  // file extension
+    QString fileName = fileInfo.fileName();  // filename
+    QString uploadUrl;  // Image upload URL
+    uploadUrl = "https://api.github.com/repos/" + p_username + "/" + p_repository + "/contents/"  +
+            QString::number(QDateTime::currentDateTime().toTime_t()) +"_" + fileName;
+    if(fileSuffix != QString::fromLocal8Bit("jpg") && fileSuffix != QString::fromLocal8Bit("png")
+            && fileSuffix != QString::fromLocal8Bit("gif")){
+        qDebug() << "Unsupported type...";
+        QString info = tr("Unsupported type: ") + fileSuffix;
+        QMessageBox::warning(NULL, tr("Github Image Hosting"), info);
+        imageUrlMap.clear();
+        if(imageUploaded){
+            githubImageBedReplaceLink(newFileContent, m_file->fetchPath());
+        }
+        return;
+    }
+
+    QNetworkRequest request;
+    QUrl url = QUrl(uploadUrl);
+    QString ptoken = "token " + p_token;
+    request.setRawHeader("Authorization", ptoken.toLocal8Bit());
+    request.setUrl(url);
+    if(reply != Q_NULLPTR) {
+        reply->deleteLater();
+    }
+
+    QString param = githubImageBedGenerateParam(p_image_path);
+    QByteArray postData;
+    postData.append(param);
+    reply = manager.put(request, postData);
+    qDebug() << "Start uploading images: " + p_image_path + " Waiting for upload to complete";
+    uploadImageStatus = true;
+    currentUploadImage = p_image_path;
+    connect(reply, &QNetworkReply::finished, this, &VGithubImageHosting::githubImageBedUploadFinished);
+}
+
+void VGithubImageHosting::githubImageBedUploadFinished()
+{
+    if (proDlg->wasCanceled()) {
+        qDebug() << "User stops uploading";
+        reply->abort();        // Stop network request
+        imageUrlMap.clear();
+        // The ones that have been uploaded successfully before still need to stay
+        if(imageUploaded){
+            githubImageBedReplaceLink(newFileContent, m_file->fetchPath());
+        }
+        return;
+    }
+
+    switch (reply->error()) {
+        case QNetworkReply::NoError:
+        {
+            QByteArray bytes = reply->readAll();
+            int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+            if(httpStatus == 201){
+                qDebug() <<  "Upload success";
+
+                QString downloadUrl;
+                QString imageName;
+                QJsonDocument doucment = QJsonDocument::fromJson(bytes);
+                if (!doucment.isNull() )
+                {
+                    if (doucment.isObject()) {
+                        QJsonObject object = doucment.object();
+                        if (object.contains("content")) {
+                            QJsonValue value = object.value("content");
+                            if (value.isObject()) {
+                                QJsonObject obj = value.toObject();
+                                if (obj.contains("download_url")) {
+                                    QJsonValue value = obj.value("download_url");
+                                    if (value.isString()) {
+                                        downloadUrl = value.toString();
+                                        qDebug() << "json decode: download_url : " << downloadUrl;
+                                        imageUploaded = true;  // On behalf of successfully uploaded images
+                                        proDlg->setValue(uploadImageCount);
+                                    }
+                                }
+                                if(obj.contains("name")){
+                                    QJsonValue value = obj.value("name");
+                                    if(value.isString()){
+                                        imageName = value.toString();
+                                    }
+                                }
+
+                                // Traverse key in imageurlmap
+                                QList<QString> klist =  imageUrlMap.keys();
+                                QString temp;
+                                for(int i=0;i<klist.count();i++)
+                                {
+                                    temp = klist[i].split("/")[1];
+                                    if(imageName.contains(temp))
+                                    {
+                                        // You can assign values in the map
+                                        imageUrlMap.insert(klist[i], downloadUrl);
+
+                                        // Replace the link in the original
+                                        newFileContent.replace(klist[i], downloadUrl);
+
+                                        break;
+                                    }
+                                }
+                                // Start calling the method.
+                                // Whether the value in the map is empty determines whether to stop.
+                                githubImageBedUploadManager();
+                            }
+                        }
+                    }
+                }
+                else{
+                    delete proDlg;
+                    imageUrlMap.clear();
+                    qDebug() << "Resolution failure!";
+                    qDebug() << "Resolution failure's json: " << bytes;
+                    if(imageUploaded){
+                        githubImageBedReplaceLink(newFileContent, m_file->fetchPath());
+                    }
+                    QString info = tr("Json decode error, Please contact the developer~");
+                    QMessageBox::warning(NULL, tr("Github Image Hosting"), info);
+                }
+            }else{
+                // If status is not 201, it means there is a problem.
+                delete proDlg;
+                imageUrlMap.clear();
+                qDebug() << "Upload failure";
+                if(imageUploaded){
+                    githubImageBedReplaceLink(newFileContent, m_file->fetchPath());
+                }
+                QString info = tr("github status code != 201, Please contact the developer~");
+                QMessageBox::warning(NULL, tr("Github Image Hosting"), info);
+            }
+            break;
+        }
+        default:
+        {
+            delete proDlg;
+            imageUrlMap.clear();
+            qDebug()<<"network error: " << reply->errorString() << " error " << reply->error();
+            QByteArray bytes = reply->readAll();
+            qDebug() << bytes;
+            int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+            qDebug() << "status: " << httpStatus;
+
+            if(imageUploaded){
+                githubImageBedReplaceLink(newFileContent, m_file->fetchPath());
+            }
+            QString info = tr("Uploading ") + currentUploadImage + tr(" \n\nNetwork error: ") +
+                    reply->errorString() + tr("\n\nPlease check the network or image size");
+            QMessageBox::warning(NULL, tr("Github Image Hosting"), info);
+        }
+    }
+}
+
+void VGithubImageHosting::githubImageBedReplaceLink(const QString p_fileContent, const QString p_filePath)
+{
+    // This function must be executed when the upload is completed or fails in the middle.
+    // Write content to file.
+    QFile file(p_filePath);
+    file.open(QIODevice::WriteOnly | QIODevice::Text);
+    file.write(p_fileContent.toUtf8());
+    file.close();
+
+    // Reset.
+    imageUrlMap.clear();
+    imageUploaded = false;
+}
+
+QString VGithubImageHosting::githubImageBedGenerateParam(const QString p_image_path){
+    // According to the requirements of GitHub interface, pictures must be in Base64 format.
+    // Image to base64.
+    QByteArray hexed;
+    QFile imgFile(p_image_path);
+    imgFile.open(QIODevice::ReadOnly);
+    hexed = imgFile.readAll().toBase64();
+
+    QString imgBase64 = hexed;
+    QJsonObject json;
+    json.insert("message", QString("updatetest"));
+    json.insert("content", imgBase64);
+
+    QJsonDocument document;
+    document.setObject(json);
+    QByteArray byteArray = document.toJson(QJsonDocument::Compact);
+    QString jsonStr(byteArray);
+    return jsonStr;
+}
+
+VWechatImageHosting::VWechatImageHosting(VFile *p_file, QWidget *p_parent)
+    :QObject(p_parent),
+     m_file(p_file)
+{
+    reply = Q_NULLPTR;
+    imageUploaded = false;
+}
+
+void VWechatImageHosting::handleUploadImageToWechatRequested()
+{
+    qDebug() << "Start processing image upload request to wechat";
+    QString appid = g_config->getAppid();
+    QString secret = g_config->getSecret();
+    if(appid.isEmpty() || secret.isEmpty())
+    {
+        qDebug() << "Please configure the Wechat image hosting first!";
+        QMessageBox::warning(NULL, tr("Wechat Image Hosting"),
+                             tr("Please configure the Wechat image hosting first!"));
+        return;
+    }
+
+    authenticateWechatImageHosting(appid, secret);
+}
+
+void VWechatImageHosting::authenticateWechatImageHosting(const QString p_appid, const QString p_secret)
+{
+    qDebug() << "Start certification";
+    // Set the mouse to wait
+    QApplication::setOverrideCursor(Qt::WaitCursor);
+    QNetworkRequest request;
+    QString auth_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+
+            p_appid.toLocal8Bit() + "&secret=" + p_secret.toLocal8Bit();
+    QUrl url = QUrl(auth_url);
+    request.setUrl(url);
+    if(reply != Q_NULLPTR) {
+        reply->deleteLater();
+    }
+    reply = manager.get(request);
+    connect(reply, &QNetworkReply::finished, this, &VWechatImageHosting::wechatImageBedAuthFinished);
+}
+
+void VWechatImageHosting::wechatImageBedAuthFinished()
+{
+    switch (reply->error()) {
+        case QNetworkReply::NoError:
+        {
+            QByteArray bytes = reply->readAll();
+            QJsonDocument document = QJsonDocument::fromJson(bytes);
+            if(!document.isNull()){
+                if(document.isObject()){
+                    QJsonObject object = document.object();
+                    if(object.contains("access_token")){
+                        QJsonValue value = object.value("access_token");
+                        if(value.isString()){
+                            qDebug() << "Authentication successful, get token";
+                            // Parsing token.
+                            wechatAccessToken = value.toString();
+
+                            qDebug() << "The current article path is: " << m_file->fetchPath();
+                            imageBasePath = m_file->fetchBasePath();
+                            newFileContent = m_file->getContent();
+
+                            QVector<ImageLink> images = VUtils::fetchImagesFromMarkdownFile(m_file,ImageLink::LocalRelativeInternal);
+                            QApplication::restoreOverrideCursor();  // Recovery pointer
+                            if(images.size() > 0)
+                            {
+                                proDlg = new QProgressDialog(tr("Uploading images to github..."),
+                                                       tr("Abort"),
+                                                       0,
+                                                       images.size(),
+                                                       NULL);
+                                proDlg->setWindowModality(Qt::WindowModal);
+                                proDlg->setWindowTitle(tr("Uploading Images To Github"));
+                                proDlg->setMinimumDuration(1);
+                                uploadImageCount = images.size();
+                                uploadImageCountIndex  = uploadImageCount;
+                                for(int i=0;i<images.size() ;i++)
+                                {
+                                    if(images[i].m_url.contains(".png") || images[i].m_url.contains(".jpg")){
+                                        imageUrlMap.insert(images[i].m_url,"");
+                                    }else{
+                                        delete proDlg;
+                                        imageUrlMap.clear();
+                                        qDebug() << "Unsupported type...";
+                                        QFileInfo file_info(images[i].m_path.toLocal8Bit());
+                                        QString file_suffix = file_info.suffix();
+                                        QString info = tr("Unsupported type: ") + file_suffix;
+                                        QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
+                                        return;
+                                    }
+                                }
+                                wechatImageBedUploadManager();
+                            }
+                            else
+                            {
+                                qDebug() << m_file->getName() << " No pictures to upload";
+                                QString info = m_file->getName() + tr(" No pictures to upload");
+                                QMessageBox::information(NULL, tr("Wechat Image Hosting"), info);
+                            }
+                        }
+                    }else{
+                        qDebug() << "Authentication failed";
+                        QString string = bytes;
+                        qDebug() << string;
+                        // You can refine the error here.
+                        QApplication::restoreOverrideCursor();
+                        if(string.contains("invalid ip")){
+                            QString ip = string.split(" ")[2];
+                            QClipboard *board = QApplication::clipboard();
+                            board->setText(ip);
+                            QMessageBox::warning(NULL, tr("Wechat Image Hosting"),
+                                                 tr("Your ip address was set to the Clipboard!") +
+                                                 tr("\nPlease add the  IP address: ") +
+                                                 ip + tr(" to the wechat ip whitelist!"));
+                        }else{
+                            QMessageBox::warning(NULL, tr("Wechat Image Hosting"),
+                                                 tr("Please check your Wechat Image Hosting parameters !!\n") +
+                                                 string);
+                        }
+                        return;
+                    }
+                }
+            }else{
+                delete proDlg;
+                imageUrlMap.clear();
+                qDebug() << "Resolution failure!";
+                qDebug() << "Resolution failure's json: " << bytes;
+                QString info = tr("Json decode error, Please contact the developer~");
+                QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
+            }
+
+            break;
+        }
+        default:
+        {
+            QApplication::restoreOverrideCursor();
+            qDebug() << "Network error: " << reply->errorString() << " error " << reply->error();
+            QString info = tr("Network error: ") + reply->errorString();
+            QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
+        }
+    }
+}
+
+void VWechatImageHosting::wechatImageBedUploadManager()
+{
+    uploadImageCountIndex--;
+
+    QString image_to_upload = "";
+    QMapIterator<QString, QString> it(imageUrlMap);
+    while(it.hasNext())
+    {
+        it.next();
+        if(it.value() == ""){
+            image_to_upload = it.key();
+            proDlg->setValue(uploadImageCount - 1 - uploadImageCountIndex);
+            proDlg->setLabelText(tr("Uploaading image: %1").arg(image_to_upload));
+            break;
+        }
+    }
+
+    if(image_to_upload == ""){
+        qDebug() << "All pictures have been uploaded";
+        // Copy content to clipboard.
+        wechatImageBedReplaceLink(newFileContent);
+        return;
+    }
+
+    QString path = imageBasePath + QDir::separator();
+    path += image_to_upload;
+    currentUploadRelativeImagePah = image_to_upload;
+    wechatImageBedUploadImage(path, wechatAccessToken);
+}
+
+void VWechatImageHosting::wechatImageBedUploadImage(const QString p_image_path, const QString p_token)
+{
+    qDebug() << "To deal with: " << p_image_path;
+    QFileInfo fileInfo(p_image_path.toLocal8Bit());
+    if(!fileInfo.exists()){
+        delete proDlg;
+        imageUrlMap.clear();
+        qDebug() << "The picture does not exist in this path: " << p_image_path.toLocal8Bit();
+        QString info = tr("The picture does not exist in this path: ") + p_image_path.toLocal8Bit();
+        QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
+        return;
+    }
+
+    QString file_suffix = fileInfo.suffix();  // File extension.
+    QString file_name = fileInfo.fileName();  // Filename.
+    if(file_suffix != QString::fromLocal8Bit("jpg") && file_suffix != QString::fromLocal8Bit("png")){
+        delete proDlg;
+        imageUrlMap.clear();
+        qDebug() << "Unsupported type...";
+        QString info = tr("Unsupported type: ") + file_suffix;
+        QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
+        return;
+    }
+
+    qint64 file_size = fileInfo.size();  // Unit is byte.
+    qDebug() << "Image size: " << file_size;
+    if(file_size > 1024*1024){
+        delete proDlg;
+        imageUrlMap.clear();
+        qDebug() << "The size of the picture is more than 1M";
+        QString info = tr("The size of the picture is more than 1M! Wechat API does not support!!");
+        QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
+        return;
+    }
+
+    QString upload_img_url = "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=" + p_token;
+
+    QNetworkRequest request;
+    request.setUrl(upload_img_url);
+    if(reply != Q_NULLPTR){
+        reply->deleteLater();
+    }
+
+    QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
+    QHttpPart imagePart;
+    imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png"));
+    QString filename = p_image_path.split(QDir::separator()).last();
+    QString contentVariant = QString("form-data; name=\"media\"; filename=\"%1\";").arg(filename);
+    imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(contentVariant));
+    QFile *file = new QFile(p_image_path);
+    if(!file->open(QIODevice::ReadOnly)){
+        qDebug() << "File open failed";
+    }
+    imagePart.setBodyDevice(file);
+    file->setParent(multiPart);
+    multiPart->append(imagePart);
+
+    // Set boundary
+    // Because boundary is quoted by QNetworkAccessManager, the wechat api is not recognized...
+    QByteArray m_boundary;
+    m_boundary.append("multipart/form-data; boundary=");
+    m_boundary.append(multiPart->boundary());
+    request.setRawHeader(QByteArray("Content-Type"), m_boundary);
+
+    reply = manager.post(request, multiPart);
+    multiPart->setParent(reply);
+
+    qDebug() << "Start uploading images: " + p_image_path + " Waiting for upload to complete";
+    uploadImageStatus=true;
+    currentUploadImage = p_image_path;
+    connect(reply, &QNetworkReply::finished, this, &VWechatImageHosting::wechatImageBedUploadFinished);
+}
+
+void VWechatImageHosting::wechatImageBedUploadFinished()
+{
+    if(proDlg->wasCanceled()){
+        qDebug() << "User stops uploading";
+        reply->abort();
+        // If the upload was successful, don't use it!!!
+        imageUrlMap.clear();
+        return;
+    }
+
+    switch (reply->error()) {
+        case QNetworkReply::NoError:
+        {
+            QByteArray bytes = reply->readAll();
+
+            QJsonDocument document = QJsonDocument::fromJson(bytes);
+            if(!document.isNull()){
+                if(document.isObject()){
+                    QJsonObject object = document.object();
+                    if(object.contains("url")){
+                        QJsonValue value = object.value("url");
+                        if(value.isString()){
+                            qDebug() << "Authentication successful, get online link";
+                            imageUploaded = true;
+                            proDlg->setValue(uploadImageCount);
+
+                            imageUrlMap.insert(currentUploadRelativeImagePah, value.toString());
+                            newFileContent.replace(currentUploadRelativeImagePah, value.toString());
+                            // Start calling the method.
+                            // Whether the value in the map is empty determines whether to stop
+                            wechatImageBedUploadManager();
+                        }
+                    }else{
+                        delete proDlg;
+                        imageUrlMap.clear();
+                        qDebug() << "Upload failure: ";
+                        QString error = bytes;
+                        qDebug() << bytes;
+                        QString info = tr("upload failed! Please contact the developer~");
+                        QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
+                    }
+                }
+            }else{
+                delete proDlg;
+                imageUrlMap.clear();
+                qDebug() << "Resolution failure!";
+                qDebug() << "Resolution failure's json: " << bytes;
+                QString info = tr("Json decode error, Please contact the developer~");
+                QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
+            }
+
+            break;
+        }
+        default:
+        {
+            delete proDlg;
+            qDebug()<<"Network error: " << reply->errorString() << " error " << reply->error();
+
+            QString info = tr("Uploading ") + currentUploadImage + tr(" \n\nNetwork error: ") +
+                    reply->errorString() + tr("\n\nPlease check the network or image size");
+            QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
+        }
+    }
+}
+
+void VWechatImageHosting::wechatImageBedReplaceLink(const QString p_file_content)
+{
+    // Write content to clipboard.
+    QClipboard *board = QApplication::clipboard();
+    board->setText(p_file_content);
+    QString url = g_config->getMarkdown2WechatToolUrl();
+    if(url.isEmpty()){
+        QMessageBox::warning(NULL, tr("Wechat Image Hosting"),
+                             tr("The article has been copied to the clipboard. Please find a text file and save it!!"));
+    }else{
+        QMessageBox::StandardButton result;
+        result = QMessageBox::question(NULL, tr("Wechat Image Hosting"),
+                                       tr("The article has been copied to the clipboard.") +
+                                       tr("Do you want to open the tool link of mark down to wechat?"),
+                                       QMessageBox::Yes|QMessageBox::No,QMessageBox::Yes);
+        if(result == QMessageBox::Yes){
+            QDesktopServices::openUrl(QUrl(url));
+        }
+    }
+    imageUrlMap.clear();
+    imageUploaded = false;  // Reset.
+}

+ 131 - 0
src/vimagehosting.h

@@ -0,0 +1,131 @@
+#ifndef VGITHUBIMAGEHOSTING_H
+#define VGITHUBIMAGEHOSTING_H
+
+#include <QObject>
+#include <QtNetwork>
+#include <QProgressDialog>
+#include <QDesktopServices>
+#include <QUrl>
+#include <QByteArray>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QApplication>
+#include <vfile.h>
+#include <QClipboard>
+
+class VGithubImageHosting : public QObject
+{
+    Q_OBJECT
+public:
+    explicit VGithubImageHosting(VFile *p_file, QWidget *p_parent = nullptr);
+
+    // GitHub identity authentication.
+    void authenticateGithubImageHosting(QString p_token);
+
+    // Upload a single image.
+    void githubImageBedUploadImage(const QString &p_username,
+                                   const QString &p_repository,
+                                   const QString &p_image_path,
+                                   const QString &p_token);
+
+    // Parameters needed to generate uploaded images.
+    QString githubImageBedGenerateParam(const QString p_image_path);
+
+    // Control image to upload.
+    void githubImageBedUploadManager();
+
+    // Replace old links with new ones for images.
+    void githubImageBedReplaceLink(const QString p_file_content, const QString p_file_path);
+
+    // Process the image upload request to GitHub.
+    void handleUploadImageToGithubRequested();
+
+public slots:
+    // GitHub image hosting identity authentication completed.
+    void githubImageBedAuthFinished();
+
+    // GitHub image hosting upload completed.
+    void githubImageBedUploadFinished();
+
+private:
+    QNetworkAccessManager manager;
+    QNetworkReply *reply;
+    QMap<QString, QString> imageUrlMap;
+    // Similar to "_v_image/".
+    QString imageBasePath;
+    // Replace the file content with the new link.
+    QString newFileContent;
+    // Whether the picture has been uploaded successfully.
+    bool imageUploaded;
+    // Image upload progress bar.
+    QProgressDialog *proDlg;
+    // Total number of images to upload.
+    int uploadImageCount;
+    int uploadImageCountIndex;
+    // Currently uploaded picture name.
+    QString currentUploadImage;
+    // Image upload status.
+    bool uploadImageStatus;
+    // Token returned after successful wechat authentication.
+    QString wechatAccessToken;
+    // Relative image path currently Uploaded.
+    QString currentUploadRelativeImagePah;
+    VFile *m_file;
+};
+
+class VWechatImageHosting : public QObject
+{
+    Q_OBJECT
+public:
+    explicit VWechatImageHosting(VFile *p_file, QWidget *p_parent = nullptr);
+
+    // Wechat identity authentication.
+    void authenticateWechatImageHosting(const QString p_appid, const QString p_secret);
+
+    // Control image to upload.
+    void wechatImageBedUploadManager();
+
+    // Replace old links with new ones for images.
+    void wechatImageBedReplaceLink(const QString p_file_content);
+
+    // Upload a single image.
+    void wechatImageBedUploadImage(const QString p_image_path, const QString p_token);
+
+    // Process image upload request to wechat.
+    void handleUploadImageToWechatRequested();
+
+public slots:
+    // Wechat mage hosting identity authentication completed.
+    void wechatImageBedAuthFinished();
+
+    // Wechat image hosting upload completed.
+    void wechatImageBedUploadFinished();
+
+private:
+    QNetworkAccessManager manager;
+    QNetworkReply *reply;
+    QMap<QString, QString> imageUrlMap;
+    // Similar to "_v_image/".
+    QString imageBasePath;
+    // Replace the file content with the new link.
+    QString newFileContent;
+    // Whether the picture has been uploaded successfully.
+    bool imageUploaded;
+    // Image upload progress bar.
+    QProgressDialog *proDlg;
+    // Total number of images to upload.
+    int uploadImageCount;
+    int uploadImageCountIndex;
+    // Currently uploaded picture name.
+    QString currentUploadImage;
+    // Image upload status
+    bool uploadImageStatus;
+    // Token returned after successful wechat authentication.
+    QString wechatAccessToken;
+    // Relative image path currently Uploaded.
+    QString currentUploadRelativeImagePah;
+    VFile *m_file;
+};
+
+#endif // VGITHUBIMAGEHOSTING_H

+ 6 - 653
src/vmdtab.cpp

@@ -113,8 +113,9 @@ void VMdTab::setupUI()
     // Setup editor when we really need it.
     m_editor = NULL;
 
-    reply = Q_NULLPTR;
-    imageUploaded = false;
+    // The following is the image hosting initialization
+    vGithubImageHosting = new VGithubImageHosting(m_file, this);
+    vWechatImageHosting = new VWechatImageHosting(m_file, this);
 
     QVBoxLayout *layout = new QVBoxLayout();
     layout->addWidget(m_splitter);
@@ -446,6 +447,7 @@ void VMdTab::setupMarkdownViewer()
             this, &VMdTab::handleWebSelectionChanged);
     connect(m_webViewer, &VWebView::requestExpandRestorePreviewArea,
             this, &VMdTab::expandRestorePreviewArea);
+
     connect(m_webViewer, &VWebView::requestUploadImageToGithub,
             this, &VMdTab::handleUploadImageToGithubRequested);
     connect(m_webViewer, &VWebView::requestUploadImageToWechat,
@@ -1512,661 +1514,12 @@ void VMdTab::handleSavePageRequested()
 
 void VMdTab::handleUploadImageToGithubRequested()
 {
-    qDebug() << "Start processing the image upload request to GitHub";
-
-    if(g_config->getpersonalAccessToken().isEmpty() || g_config->getReposName().isEmpty() || g_config->getUserName().isEmpty())
-    {
-        qDebug() << "Please configure the GitHub image hosting first!";
-        QMessageBox::warning(NULL, tr("Github Image Hosting"), tr("Please configure the GitHub image hosting first!"));
-        return;
-    }
-
-    authenticateGithubImageHosting(g_config->getpersonalAccessToken());
-}
-
-void VMdTab::authenticateGithubImageHosting(QString p_token)
-{
-    qDebug() << "start the authentication process ";
-    QApplication::setOverrideCursor(Qt::WaitCursor);
-    QNetworkRequest request;
-    QUrl url = QUrl("https://api.github.com");
-    QString ptoken = "token " + p_token;
-    request.setRawHeader("Authorization", ptoken.toLocal8Bit());
-    request.setUrl(url);
-    if(reply != Q_NULLPTR) {
-        reply->deleteLater();
-    }
-    reply = manager.get(request);
-    connect(reply, &QNetworkReply::finished, this, &VMdTab::githubImageBedAuthFinished);
-}
-
-void VMdTab::githubImageBedAuthFinished()
-{
-    switch (reply->error()) {
-    case QNetworkReply::NoError:
-    {
-        QByteArray bytes = reply->readAll();
-
-        if(bytes.contains("Bad credentials")){
-            qDebug() << "Authentication failed";
-            QApplication::restoreOverrideCursor();  // Recovery pointer
-            QMessageBox::warning(NULL, tr("Github Image Hosting"), tr("Bad credentials!! Please check your Github Image Hosting parameters !!"));
-            return;
-        }else{
-            qDebug() << "Authentication completed";
-
-            qDebug() << "The current article path is: " << m_file->fetchPath();
-            imageBasePath = m_file->fetchBasePath();
-            newFileContent = m_file->getContent();
-
-            QVector<ImageLink> images = VUtils::fetchImagesFromMarkdownFile(m_file,ImageLink::LocalRelativeInternal);
-            QApplication::restoreOverrideCursor();  // Recovery pointer
-            if(images.size() > 0)
-            {
-
-                proDlg = new QProgressDialog(tr("Uploading images to github..."),
-                                       tr("Abort"),
-                                       0,
-                                       images.size(),
-                                       this);
-                proDlg->setWindowModality(Qt::WindowModal);
-                proDlg->setWindowTitle(tr("Uploading Images To Github"));
-                proDlg->setMinimumDuration(1);
-                uploadImageCount = images.size();
-                uploadImageCountIndex  = uploadImageCount;
-                for(int i=0;i<images.size() ;i++)
-                {
-                    if(images[i].m_url.contains(".png") || images[i].m_url.contains(".jpg")|| images[i].m_url.contains(".gif")){
-                        imageUrlMap.insert(images[i].m_url,"");
-                    }else{
-                        delete proDlg;
-                        imageUrlMap.clear();
-                        qDebug() << "Unsupported type...";
-                        QFileInfo fileInfo(images[i].m_path.toLocal8Bit());
-                        QString fileSuffix = fileInfo.suffix();
-                        QString info = tr("Unsupported type: ") + fileSuffix;
-                        QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
-                        return;
-                    }
-                }
-                githubImageBedUploadManager();
-            }
-            else
-            {
-                qDebug() << m_file->getName() << " No images to upload";
-                QString info = m_file->getName() + " No pictures to upload";
-                QMessageBox::information(NULL, tr("Github Image Hosting"), info);
-            }
-        }
-        break;
-    }
-    default:
-    {
-        QApplication::restoreOverrideCursor();  // Recovery pointer
-        qDebug() << "Network error: " << reply->errorString() << " error " << reply->error();
-        QString info = tr("Network error: ") + reply->errorString();
-        QMessageBox::warning(NULL, tr("Github Image Hosting"), info);
-    }
-    }
-}
-
-void VMdTab::githubImageBedUploadManager()
-{
-    uploadImageCountIndex--;
-
-    QString imageToUpload = "";
-    QMapIterator<QString, QString> it(imageUrlMap);
-    while(it.hasNext())
-    {
-        it.next();
-        if(it.value() == ""){
-            imageToUpload = it.key();
-            proDlg->setValue(uploadImageCount - 1 - uploadImageCountIndex);
-            proDlg->setLabelText(tr("Uploaading image: %1").arg(imageToUpload));
-            break;
-        }
-    }
-
-    if(imageToUpload == ""){
-        qDebug() << "All images have been uploaded";
-        githubImageBedReplaceLink(newFileContent, m_file->fetchPath());
-        return;
-    }
-
-    if(g_config->getpersonalAccessToken().isEmpty() || g_config->getReposName().isEmpty() || g_config->getUserName().isEmpty())
-    {
-        qDebug() << "Please configure the GitHub image hosting first!";
-        QMessageBox::warning(NULL, tr("Github Image Hosting"), tr("Please configure the GitHub image hosting first!"));
-        imageUrlMap.clear();
-        return;
-    }
-
-    QString path = imageBasePath + QDir::separator();
-    path += imageToUpload;
-    githubImageBedUploadImage(g_config->getUserName(), g_config->getReposName(), path, g_config->getpersonalAccessToken());
-}
-
-void VMdTab::githubImageBedUploadImage(QString username, QString repository, QString imagePath, QString token)
-{
-    QFileInfo fileInfo(imagePath.toLocal8Bit());
-    if(!fileInfo.exists()){
-        qDebug() << "The picture does not exist in this path: " << imagePath.toLocal8Bit();
-        QString info = tr("The picture does not exist in this path: ") + imagePath.toLocal8Bit();
-        QMessageBox::warning(NULL, tr("Github Image Hosting"), info);
-        imageUrlMap.clear();
-        if(imageUploaded){
-            githubImageBedReplaceLink(newFileContent, m_file->fetchPath());
-        }
-        return;
-    }
-
-    QString fileSuffix = fileInfo.suffix();  // file extension
-    QString fileName = fileInfo.fileName();  // filename
-    QString uploadUrl;  // Image upload URL
-    uploadUrl = "https://api.github.com/repos/" + username + "/" + repository + "/contents/"  +  QString::number(QDateTime::currentDateTime().toTime_t()) +"_" + fileName;
-    if(fileSuffix != QString::fromLocal8Bit("jpg") && fileSuffix != QString::fromLocal8Bit("png") && fileSuffix != QString::fromLocal8Bit("gif")){
-        qDebug() << "Unsupported type...";
-        QString info = tr("Unsupported type: ") + fileSuffix;
-        QMessageBox::warning(NULL, tr("Github Image Hosting"), info);
-        imageUrlMap.clear();
-        if(imageUploaded){
-            githubImageBedReplaceLink(newFileContent, m_file->fetchPath());
-        }
-        return;
-    }
-
-    QNetworkRequest request;
-    QUrl url = QUrl(uploadUrl);
-    QString ptoken = "token " + token;
-    request.setRawHeader("Authorization", ptoken.toLocal8Bit());
-    request.setUrl(url);
-    if(reply != Q_NULLPTR) {
-        reply->deleteLater();
-    }
-
-    QString param = githubImageBedGenerateParam(imagePath);
-    QByteArray postData;
-    postData.append(param);
-    reply = manager.put(request, postData);
-    qDebug() << "Start uploading images: " + imagePath + " Waiting for upload to complete";
-    uploadImageStatus = true;
-    currentUploadImage = imagePath;
-    connect(reply, &QNetworkReply::finished, this, &VMdTab::githubImageBedUploadFinished);
-}
-
-void VMdTab::githubImageBedUploadFinished()
-{
-    if (proDlg->wasCanceled()) {
-        qDebug() << "User stops uploading";
-        reply->abort();        // Stop network request
-        imageUrlMap.clear();
-        // The ones that have been uploaded successfully before still need to stay
-        if(imageUploaded){
-            githubImageBedReplaceLink(newFileContent, m_file->fetchPath());
-        }
-        return;
-    }
-
-    switch (reply->error()) {
-        case QNetworkReply::NoError:
-        {
-            QByteArray bytes = reply->readAll();
-            int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-            if(httpStatus == 201){
-                qDebug() <<  "Upload success";
-
-                QString downloadUrl;
-                QString imageName;
-                QJsonDocument doucment = QJsonDocument::fromJson(bytes);
-                if (!doucment.isNull() )
-                {
-                    if (doucment.isObject()) {
-                        QJsonObject object = doucment.object();
-                        if (object.contains("content")) {
-                            QJsonValue value = object.value("content");
-                            if (value.isObject()) {
-                                QJsonObject obj = value.toObject();
-                                if (obj.contains("download_url")) {
-                                    QJsonValue value = obj.value("download_url");
-                                    if (value.isString()) {
-                                        downloadUrl = value.toString();
-                                        qDebug() << "json decode: download_url : " << downloadUrl;
-                                        imageUploaded = true;  // On behalf of successfully uploaded images
-                                        proDlg->setValue(uploadImageCount);
-                                    }
-                                }
-                                if(obj.contains("name")){
-                                    QJsonValue value = obj.value("name");
-                                    if(value.isString()){
-                                        imageName = value.toString();
-                                    }
-                                }
-
-                                // Traverse key in imageurlmap
-                                QList<QString> klist =  imageUrlMap.keys();
-                                QString temp;
-                                for(int i=0;i<klist.count();i++)
-                                {
-
-                                    temp = klist[i].split("/")[1];
-                                    if(imageName.contains(temp))
-                                    {
-                                        // You can assign values in the map
-                                        imageUrlMap.insert(klist[i], downloadUrl);
-
-                                        // Replace the link in the original
-                                        newFileContent.replace(klist[i], downloadUrl);
-
-                                        break;
-                                    }
-                                }
-                                // Start calling the method. Whether the value in the map is empty determines whether to stop
-                                githubImageBedUploadManager();
-                            }
-                        }
-                    }
-                }
-                else{
-                    delete proDlg;
-                    imageUrlMap.clear();
-                    qDebug() << "Resolution failure!";
-                    qDebug() << "Resolution failure's json: " << bytes;
-                    if(imageUploaded){
-                        githubImageBedReplaceLink(newFileContent, m_file->fetchPath());
-                    }
-                    QString info = tr("Json decode error, Please contact the developer~");
-                    QMessageBox::warning(NULL, tr("Github Image Hosting"), info);
-                }
-
-
-            }else{
-                // If status is not 201, it means there is a problem
-                delete proDlg;
-                imageUrlMap.clear();
-                qDebug() << "Upload failure";
-                if(imageUploaded){
-                    githubImageBedReplaceLink(newFileContent, m_file->fetchPath());
-                }
-                QString info = tr("github status code != 201, Please contact the developer~");
-                QMessageBox::warning(NULL, tr("Github Image Hosting"), info);
-            }
-            break;
-        }
-        default:
-        {
-            delete proDlg;
-            imageUrlMap.clear();
-            qDebug()<<"network error: " << reply->errorString() << " error " << reply->error();
-            QByteArray bytes = reply->readAll();
-            qDebug() << bytes;
-            int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-            qDebug() << "status: " << httpStatus;
-
-            if(imageUploaded){
-                githubImageBedReplaceLink(newFileContent, m_file->fetchPath());
-            }
-            QString info = tr("Uploading ") + currentUploadImage + tr(" \n\nNetwork error: ") + reply->errorString() + tr("\n\nPlease check the network or image size");
-            QMessageBox::warning(NULL, tr("Github Image Hosting"), info);
-        }
-    }
-}
-
-void VMdTab::githubImageBedReplaceLink(QString fileContent, QString filePath)
-{
-    // This function must be executed when the upload is completed or fails in the middle
-    // Write content to file
-    QFile file(filePath);
-    file.open(QIODevice::WriteOnly | QIODevice::Text);
-    file.write(fileContent.toUtf8());
-    file.close();
-    // Reset
-    imageUrlMap.clear();
-    imageUploaded = false;
-}
-
-QString VMdTab::githubImageBedGenerateParam(QString imagePath){
-    // According to the requirements of GitHub interface, pictures must be in Base64 format
-    // img to base64
-    QByteArray hexed;
-    QFile imgFile(imagePath);
-    imgFile.open(QIODevice::ReadOnly);
-    hexed = imgFile.readAll().toBase64();
-
-    QString imgBase64 = hexed;  // Base64 encoding of images
-    QJsonObject json;
-    json.insert("message", QString("updatetest"));
-    json.insert("content", imgBase64);
-
-    QJsonDocument document;
-    document.setObject(json);
-    QByteArray byteArray = document.toJson(QJsonDocument::Compact);
-    QString jsonStr(byteArray);
-    return jsonStr;
+      vGithubImageHosting->handleUploadImageToGithubRequested();
 }
 
 void VMdTab::handleUploadImageToWechatRequested()
 {
-    qDebug() << "Start processing image upload request to wechat";
-    QString appid = g_config->getAppid();
-    QString secret = g_config->getSecret();
-    if(appid.isEmpty() || secret.isEmpty())
-    {
-        qDebug() << "Please configure the Wechat image hosting first!";
-        QMessageBox::warning(NULL, tr("Wechat Image Hosting"), tr("Please configure the Wechat image hosting first!"));
-        return;
-    }
-
-    authenticateWechatImageHosting(appid, secret);
-}
-
-void VMdTab::authenticateWechatImageHosting(QString appid, QString secret)
-{
-    qDebug() << "Start certification";
-    QApplication::setOverrideCursor(Qt::WaitCursor); // Set the mouse to wait
-    QNetworkRequest request;
-    QString auth_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+ appid.toLocal8Bit() + "&secret=" + secret.toLocal8Bit();
-    QUrl url = QUrl(auth_url);
-//    request.setRawHeader("grant_type", "client_credential");
-//    request.setRawHeader("appid", appid.toLocal8Bit());
-//    request.setRawHeader("secret", secret.toLocal8Bit());
-    request.setUrl(url);
-    if(reply != Q_NULLPTR) {
-        reply->deleteLater();
-    }
-    reply = manager.get(request);
-    connect(reply, &QNetworkReply::finished, this, &VMdTab::wechatImageBedAuthFinished);
-}
-
-void VMdTab::wechatImageBedAuthFinished()
-{
-    switch (reply->error()) {
-        case QNetworkReply::NoError:
-        {
-            QByteArray bytes = reply->readAll();
-            QJsonDocument document = QJsonDocument::fromJson(bytes);
-            if(!document.isNull()){
-                if(document.isObject()){
-                    QJsonObject object = document.object();
-                    if(object.contains("access_token")){
-                        QJsonValue value = object.value("access_token");
-                        if(value.isString()){
-                            qDebug() << "Authentication successful, get token";
-                            // Parsing token
-                            wechatAccessToken = value.toString();
-
-                            qDebug() << "The current article path is: " << m_file->fetchPath();
-                            imageBasePath = m_file->fetchBasePath();
-                            newFileContent = m_file->getContent();
-
-                            QVector<ImageLink> images = VUtils::fetchImagesFromMarkdownFile(m_file,ImageLink::LocalRelativeInternal);
-                            QApplication::restoreOverrideCursor();  // Recovery pointer
-                            if(images.size() > 0)
-                            {
-
-                                proDlg = new QProgressDialog(tr("Uploading images to github..."),
-                                                       tr("Abort"),
-                                                       0,
-                                                       images.size(),
-                                                       this);
-                                proDlg->setWindowModality(Qt::WindowModal);
-                                proDlg->setWindowTitle(tr("Uploading Images To Github"));
-                                proDlg->setMinimumDuration(1);
-                                uploadImageCount = images.size();
-                                uploadImageCountIndex  = uploadImageCount;
-                                for(int i=0;i<images.size() ;i++)
-                                {
-                                    if(images[i].m_url.contains(".png") || images[i].m_url.contains(".jpg")){
-                                        imageUrlMap.insert(images[i].m_url,"");
-                                    }else{
-                                        delete proDlg;
-                                        imageUrlMap.clear();
-                                        qDebug() << "Unsupported type...";
-                                        QFileInfo file_info(images[i].m_path.toLocal8Bit());
-                                        QString file_suffix = file_info.suffix();
-                                        QString info = tr("Unsupported type: ") + file_suffix;
-                                        QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
-                                        return;
-                                    }
-                                }
-                                wechatImageBedUploadManager();
-                            }
-                            else
-                            {
-                                qDebug() << m_file->getName() << " No pictures to upload";
-                                QString info = m_file->getName() + tr(" No pictures to upload");
-                                QMessageBox::information(NULL, tr("Wechat Image Hosting"), info);
-                            }
-                        }
-                    }else{
-                        qDebug() << "Authentication failed";
-                        QString string = bytes;
-                        qDebug() << string;
-                        // You can refine the error here
-                        QApplication::restoreOverrideCursor();
-                        if(string.contains("invalid ip")){
-                            QString ip = string.split(" ")[2];
-                            QClipboard *board = QApplication::clipboard();
-                            board->setText(ip);
-                            QMessageBox::warning(NULL, tr("Wechat Image Hosting"), tr("Your ip address was set to the Clipboard! \nPlease add the  IP address: ") + ip + tr(" to the wechat ip whitelist!"));
-                        }else{
-                            QMessageBox::warning(NULL, tr("Wechat Image Hosting"), tr("Please check your Wechat Image Hosting parameters !!\n") + string);
-                        }
-                        return;
-                    }
-                }
-            }else{
-                delete proDlg;
-                imageUrlMap.clear();
-                qDebug() << "Resolution failure!";
-                qDebug() << "Resolution failure's json: " << bytes;
-                QString info = tr("Json decode error, Please contact the developer~");
-                QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
-            }
-
-
-            break;
-        }
-        default:
-        {
-            QApplication::restoreOverrideCursor();
-            qDebug() << "Network error: " << reply->errorString() << " error " << reply->error();
-            QString info = tr("Network error: ") + reply->errorString();
-            QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
-        }
-    }
-}
-
-void VMdTab::wechatImageBedUploadManager()
-{
-    uploadImageCountIndex--;
-
-    QString image_to_upload = "";
-    QMapIterator<QString, QString> it(imageUrlMap);
-    while(it.hasNext())
-    {
-        it.next();
-        if(it.value() == ""){
-            image_to_upload = it.key();
-            proDlg->setValue(uploadImageCount - 1 - uploadImageCountIndex);
-            proDlg->setLabelText(tr("Uploaading image: %1").arg(image_to_upload));
-            break;
-        }
-
-    }
-
-    if(image_to_upload == ""){
-        qDebug() << "All pictures have been uploaded";
-        // Copy content to clipboard
-        wechatImageBedReplaceLink(newFileContent, m_file->fetchPath());
-        return;
-    }
-
-    QString path = imageBasePath + QDir::separator();
-    path += image_to_upload;
-    currentUploadRelativeImagePah = image_to_upload;
-    wechatImageBedUploadImage(path, wechatAccessToken);
-}
-
-void VMdTab::wechatImageBedUploadImage(QString image_path, QString token)
-{
-    qDebug() << "To deal with: " << image_path;
-    QFileInfo fileInfo(image_path.toLocal8Bit());
-    if(!fileInfo.exists()){
-        delete proDlg;
-        imageUrlMap.clear();
-        qDebug() << "The picture does not exist in this path: " << image_path.toLocal8Bit();
-        QString info = tr("The picture does not exist in this path: ") + image_path.toLocal8Bit();
-        QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
-        return;
-    }
-
-    QString file_suffix = fileInfo.suffix();  // File extension
-    QString file_name = fileInfo.fileName();  // filename
-    if(file_suffix != QString::fromLocal8Bit("jpg") && file_suffix != QString::fromLocal8Bit("png")){
-        delete proDlg;
-        imageUrlMap.clear();
-        qDebug() << "Unsupported type...";
-        QString info = tr("Unsupported type: ") + file_suffix;
-        QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
-        return;
-    }
-
-    qint64 file_size = fileInfo.size();  // Unit is byte
-    qDebug() << "Image size: " << file_size;
-    if(file_size > 1024*1024){
-        delete proDlg;
-        imageUrlMap.clear();
-        qDebug() << "The size of the picture is more than 1M";
-        QString info = tr("The size of the picture is more than 1M! Wechat API does not support!!");
-        QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
-        return;
-    }
-
-    QString upload_img_url = "https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=" + token;
-
-    QNetworkRequest request;
-    request.setUrl(upload_img_url);
-    if(reply != Q_NULLPTR){
-        reply->deleteLater();
-    }
-
-    QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
-    QHttpPart imagePart;
-    imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png"));
-    QString filename = image_path.split(QDir::separator()).last();
-    QString contentVariant = QString("form-data; name=\"media\"; filename=\"%1\";").arg(filename);
-    imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(contentVariant));
-    QFile *file = new QFile(image_path);
-    if(!file->open(QIODevice::ReadOnly)){
-        qDebug() << "File open failed";
-    }
-    imagePart.setBodyDevice(file);
-    file->setParent(multiPart);
-    multiPart->append(imagePart);
-
-    // Set boundary
-    // Because boundary is quoted by QNetworkAccessManager, the wechat api is not recognized...
-    QByteArray m_boundary;
-    m_boundary.append("multipart/form-data; boundary=");
-    m_boundary.append(multiPart->boundary());
-    request.setRawHeader(QByteArray("Content-Type"), m_boundary);
-
-    reply = manager.post(request, multiPart);
-    multiPart->setParent(reply);
-
-
-    qDebug() << "Start uploading images: " + image_path + " Waiting for upload to complete";
-    uploadImageStatus=true;
-    currentUploadImage = image_path;
-    connect(reply, &QNetworkReply::finished, this, &VMdTab::wechatImageBedUploadFinished);
-
-}
-
-void VMdTab::wechatImageBedUploadFinished()
-{
-    if(proDlg->wasCanceled()){
-        qDebug() << "User stops uploading";
-        reply->abort();
-        // If the upload was successful, don't use it!!!
-        imageUrlMap.clear();
-        return;
-    }
-
-    switch (reply->error()) {
-        case QNetworkReply::NoError:
-        {
-            QByteArray bytes = reply->readAll();
-
-            //qDebug() << "The returned contents are as follows: ";
-            //QString a = bytes;
-            //qDebug() << qPrintable(a);
-
-            QJsonDocument document = QJsonDocument::fromJson(bytes);
-            if(!document.isNull()){
-                if(document.isObject()){
-                    QJsonObject object = document.object();
-                    if(object.contains("url")){
-                        QJsonValue value = object.value("url");
-                        if(value.isString()){
-                            qDebug() << "Authentication successful, get online link";
-                            imageUploaded = true;
-                            proDlg->setValue(uploadImageCount);
-
-                            imageUrlMap.insert(currentUploadRelativeImagePah, value.toString());
-                            newFileContent.replace(currentUploadRelativeImagePah, value.toString());
-                            // Start calling the method. Whether the value in the map is empty determines whether to stop
-                            wechatImageBedUploadManager();
-                        }
-                    }else{
-                        delete proDlg;
-                        imageUrlMap.clear();
-                        qDebug() << "Upload failure: ";
-                        QString error = bytes;
-                        qDebug() << bytes;
-                        QString info = tr("upload failed! Please contact the developer~");
-                        QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
-                    }
-                }
-            }else{
-                delete proDlg;
-                imageUrlMap.clear();
-                qDebug() << "Resolution failure!";
-                qDebug() << "Resolution failure's json: " << bytes;
-                QString info = tr("Json decode error, Please contact the developer~");
-                QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
-            }
-
-            break;
-        }
-        default:
-        {
-            delete proDlg;
-            qDebug()<<"Network error: " << reply->errorString() << " error " << reply->error();
-
-            QString info = tr("Uploading ") + currentUploadImage + tr(" \n\nNetwork error: ") + reply->errorString() + tr("\n\nPlease check the network or image size");
-            QMessageBox::warning(NULL, tr("Wechat Image Hosting"), info);
-
-        }
-
-    }
-}
-void VMdTab::wechatImageBedReplaceLink(QString file_content, QString file_path)
-{
-    // Write content to clipboard
-    QClipboard *board = QApplication::clipboard();
-    board->setText(file_content);
-    QString url = g_config->getMarkdown2WechatToolUrl();
-    if(url.isEmpty()){
-        QMessageBox::warning(NULL, tr("Wechat Image Hosting"), tr("The article has been copied to the clipboard. Please find a text file and save it!!"));
-    }else{
-        QMessageBox::StandardButton result;
-        result = QMessageBox::question(this, tr("Wechat Image Hosting"), tr("The article has been copied to the clipboard. Do you want to open the tool link of mark down to wechat?"), QMessageBox::Yes|QMessageBox::No,QMessageBox::Yes);
-        if(result == QMessageBox::Yes){
-            QDesktopServices::openUrl(QUrl(url));
-        }
-    }
-    imageUrlMap.clear();
-    imageUploaded = false;  // reset
+    vWechatImageHosting->handleUploadImageToWechatRequested();
 }
 
 VWordCountInfo VMdTab::fetchWordCountInfo(bool p_editMode) const

+ 3 - 58
src/vmdtab.h

@@ -1,16 +1,14 @@
 #ifndef VMDTAB_H
 #define VMDTAB_H
 
-#include <QtNetwork>
 #include <QString>
 #include <QPointer>
 #include <QSharedPointer>
-#include <QProgressDialog>
-#include <QDesktopServices>
 #include "vedittab.h"
 #include "vconstants.h"
 #include "vmarkdownconverter.h"
 #include "vconfigmanager.h"
+#include "vimagehosting.h"
 
 class VWebView;
 class VDocument;
@@ -110,27 +108,6 @@ public:
 
     bool expandRestorePreviewArea();
 
-    // github image hosting
-    // GitHub identity authentication
-    void authenticateGithubImageHosting(QString p_token);
-    // Upload a single image
-    void githubImageBedUploadImage(QString username,QString repository,QString image_path,QString token);
-    // Parameters needed to generate uploaded images
-    QString githubImageBedGenerateParam(QString image_path);
-    // Control image upload
-    void githubImageBedUploadManager();
-    // Replace old links with new ones for images
-    void githubImageBedReplaceLink(QString file_content, QString file_path);
-
-    // wechat image hosting
-    void authenticateWechatImageHosting(QString appid, QString secret);
-    // Control image upload
-    void wechatImageBedUploadManager();
-    // Replace old links with new ones for images
-    void wechatImageBedReplaceLink(QString file_content, QString file_path);
-    // Upload a single image
-    void wechatImageBedUploadImage(QString image_path,QString token);
-
 public slots:
     // Enter edit mode.
     void editFile() Q_DECL_OVERRIDE;
@@ -188,21 +165,9 @@ private slots:
     // Process the image upload request to GitHub
     void handleUploadImageToGithubRequested();
 
-    // GitHub image hosting identity authentication completed
-    void githubImageBedAuthFinished();
-
-    // GitHub image hosting upload completed
-    void githubImageBedUploadFinished();
-
     // Process image upload request to wechat
     void handleUploadImageToWechatRequested();
 
-    // Wechat mage hosting identity authentication completed
-    void wechatImageBedAuthFinished();
-
-    // Wechat image hosting upload completed
-    void wechatImageBedUploadFinished();
-
 private:
     enum TabReady { None = 0, ReadMode = 0x1, EditMode = 0x2 };
 
@@ -320,28 +285,8 @@ private:
 
     int m_documentID;
 
-    QNetworkAccessManager manager;
-    QNetworkReply *reply;
-    QMap<QString, QString> imageUrlMap;
-    // Similar to _v_image/
-    QString imageBasePath;
-    // Replace the file content with the new link
-    QString newFileContent;
-    // Whether the picture has been uploaded successfully
-    bool imageUploaded;
-    // Image upload progress bar
-    QProgressDialog *proDlg;
-    // Total number of images to upload
-    int uploadImageCount;
-    int uploadImageCountIndex;
-    // Currently uploaded picture name
-    QString currentUploadImage;
-    // Image upload status
-    bool uploadImageStatus;
-    // Token returned after successful wechat authentication
-    QString wechatAccessToken;
-    // Relative image path currently Uploaded
-    QString currentUploadRelativeImagePah;
+    VGithubImageHosting *vGithubImageHosting;
+    VWechatImageHosting *vWechatImageHosting;
 };
 
 inline VMdEditor *VMdTab::getEditor()