• Qt 使用QtWebApp搭建Http服务器


    一、QtWebApp源码下载

    a 、下载地址

    http://www.stefanfrings.de/qtwebapp/QtWebApp.zip

    b、 源码目录

    在这里插入图片描述

    二、http服务器搭建

    a、使用qt creater新建一个项目

    在这里插入图片描述

    b、将QtWebApp的源码拷贝到工程中

    在这里插入图片描述

    c、新建一个httpServer的类,继承stefanfrings::HttpRequestHandler

    需要包含相关头文件

    #include <QObject>
    #include "httpserver/httprequesthandler.h"
    #include <QSettings>
    #include "httpserver/httplistener.h"	//新增代码
    #include "httpserver/httprequesthandler.h"	//新增代码
    #include "httpserver/staticfilecontroller.h"
    #include "httpserver.h"
    #include <QFile>
    #include <QFileInfo>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    httpServer.h

    #ifndef HTTPSERVER_H
    #define HTTPSERVER_H
    
    #include <QObject>
    #include "httpserver/httprequesthandler.h"
    #include <QSettings>
    #include "httpserver/httplistener.h"	//新增代码
    #include "httpserver/httprequesthandler.h"	//新增代码
    #include "httpserver/staticfilecontroller.h"
    #include "httpserver.h"
    #include <QFile>
    #include <QFileInfo>
    
    class HttpServer : public stefanfrings::HttpRequestHandler
    {
        Q_OBJECT
    public:
        explicit HttpServer(QObject *parent = nullptr);
        void service(stefanfrings::HttpRequest& request, stefanfrings::HttpResponse& response);
    
        /** Encoding of text files */
        QString encoding;
    
        /** Root directory of documents */
        QString docroot;
    
        /** Maximum age of files in the browser cache */
        int maxAge;
    
        struct CacheEntry {
            QByteArray document;
            qint64 created;
            QByteArray filename;
        };
    
        /** Timeout for each cached file */
        int cacheTimeout;
    
        /** Maximum size of files in cache, larger files are not cached */
        int maxCachedFileSize;
    
        /** Cache storage */
        QCache<QString,CacheEntry> cache;
    
        /** Used to synchronize cache access for threads */
        QMutex mutex;
    };
    
    #endif // HTTPSERVER_H
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    httpServer.cpp

    #include "httpserver.h"
    
    HttpServer::HttpServer(QObject *parent)
        : HttpRequestHandler{parent}
    {
    
        QString configFileName=":/new/prefix1/webapp.ini";
        QSettings* listenerSettings=new QSettings(configFileName, QSettings::IniFormat, this);
        listenerSettings->beginGroup("listener");	//新增代码
    
        new stefanfrings::HttpListener(listenerSettings, this, this);	//新增代码
    
        docroot = "E:/Qt/learning/httpServer/HttpServer";
    }
    
    void HttpServer::service(stefanfrings::HttpRequest &request, stefanfrings::HttpResponse &response)
    {
        QByteArray path=request.getPath();
        // Check if we have the file in cache
        qint64 now=QDateTime::currentMSecsSinceEpoch();
        mutex.lock();
        CacheEntry* entry=cache.object(path);
        if (entry && (cacheTimeout==0 || entry->created>now-cacheTimeout))
        {
            QByteArray document=entry->document; //copy the cached document, because other threads may destroy the cached entry immediately after mutex unlock.
            QByteArray filename=entry->filename;
            mutex.unlock();
            qDebug("StaticFileController: Cache hit for %s",path.data());
            response.setHeader("Content-Type", "application/x-zip-compressed");
            response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000));
            response.write(document,true);
        }
        else
        {
            mutex.unlock();
            // The file is not in cache.
            qDebug("StaticFileController: Cache miss for %s",path.data());
            // Forbid access to files outside the docroot directory
            if (path.contains("/.."))
            {
                qWarning("StaticFileController: detected forbidden characters in path %s",path.data());
                response.setStatus(403,"forbidden");
                response.write("403 forbidden",true);
                return;
            }
            // If the filename is a directory, append index.html.
            if (QFileInfo(docroot+path).isDir())
            {
                response.setStatus(404,"not found");
                response.write("404 not found",true);
                return;
            }
            // Try to open the file
            QFile file(docroot+path);
            qDebug("StaticFileController: Open file %s",qPrintable(file.fileName()));
            if (file.open(QIODevice::ReadOnly))
            {
                response.setHeader("Content-Type", "application/x-zip-compressed");
                response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000));
                response.setHeader("Content-Length",QByteArray::number(file.size()));
                if (file.size()<=maxCachedFileSize)
                {
                    // Return the file content and store it also in the cache
                    entry=new CacheEntry();
                    while (!file.atEnd() && !file.error())
                    {
                        QByteArray buffer=file.read(65536);
                        response.write(buffer);
                        entry->document.append(buffer);
                    }
                    entry->created=now;
                    entry->filename=path;
                    mutex.lock();
                    cache.insert(request.getPath(),entry,entry->document.size());
                    mutex.unlock();
                }
                else
                {
                    // Return the file content, do not store in cache
                    while (!file.atEnd() && !file.error())
                    {
                        response.write(file.read(65536));
                    }
                }
                file.close();
            }
            else {
                if (file.exists())
                {
                    qWarning("StaticFileController: Cannot open existing file %s for reading",qPrintable(file.fileName()));
                    response.setStatus(403,"forbidden");
                    response.write("403 forbidden",true);
                }
                else
                {
                    response.setStatus(404,"not found");
                    response.write("404 not found",true);
                }
            }
        }
    
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104

    程序中

        QString configFileName=":/new/prefix1/webapp.ini";
        QSettings* listenerSettings=new QSettings(configFileName, QSettings::IniFormat, this);
        listenerSettings->beginGroup("listener");	//新增代码
    
        new stefanfrings::HttpListener(listenerSettings, this, this);	//新增代码
    
        docroot = "E:/Qt/learning/httpServer/HttpServer";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    configFileName为配置文件内容如下:

    [listener]
    ;host=127.0.0.1
    port=8080
    minThreads=4
    maxThreads=100
    cleanupInterval=60000
    readTimeout=60000
    maxRequestSize=16000
    maxMultiPartSize=10000000
    
    [docroot]
    path=E:/Qt/learning/httpServer/HttpServer
    encoding=UTF-8
    maxAge=60000
    cacheTime=60000
    cacheSize=1000000
    maxCachedFileSize=65536
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    docroot 为文件的根目录

    三、启动http服务器

    在mainwindow中新建一个HttpServer 变量,然后实例化,
    在这里插入图片描述
    在这里插入图片描述
    此时http服务器已经启动了。

    四、获取http服务器文件

    a、打开浏览器输入http://localhost:8080/filename就能下载到文件名为filename的文件了。

    b、我的程序中只实现了zip文件,如果是其他类型文件需要修改一下地方

    在这里插入图片描述

    在这里插入图片描述

    void StaticFileController::setContentType(const QString fileName, HttpResponse &response) const
    {
        if (fileName.endsWith(".png"))
        {
            response.setHeader("Content-Type", "image/png");
        }
        else if (fileName.endsWith(".jpg"))
        {
            response.setHeader("Content-Type", "image/jpeg");
        }
        else if (fileName.endsWith(".gif"))
        {
            response.setHeader("Content-Type", "image/gif");
        }
        else if (fileName.endsWith(".pdf"))
        {
            response.setHeader("Content-Type", "application/pdf");
        }
        else if (fileName.endsWith(".txt"))
        {
            response.setHeader("Content-Type", qPrintable("text/plain; charset="+encoding));
        }
        else if (fileName.endsWith(".html") || fileName.endsWith(".htm"))
        {
            response.setHeader("Content-Type", qPrintable("text/html; charset="+encoding));
        }
        else if (fileName.endsWith(".css"))
        {
            response.setHeader("Content-Type", "text/css");
        }
        else if (fileName.endsWith(".js"))
        {
            response.setHeader("Content-Type", "text/javascript");
        }
        else if (fileName.endsWith(".svg"))
        {
            response.setHeader("Content-Type", "image/svg+xml");
        }
        else if (fileName.endsWith(".woff"))
        {
            response.setHeader("Content-Type", "font/woff");
        }
        else if (fileName.endsWith(".woff2"))
        {
            response.setHeader("Content-Type", "font/woff2");
        }
        else if (fileName.endsWith(".ttf"))
        {
            response.setHeader("Content-Type", "application/x-font-ttf");
        }
        else if (fileName.endsWith(".eot"))
        {
            response.setHeader("Content-Type", "application/vnd.ms-fontobject");
        }
        else if (fileName.endsWith(".otf"))
        {
            response.setHeader("Content-Type", "application/font-otf");
        }
        else if (fileName.endsWith(".json"))
        {
            response.setHeader("Content-Type", "application/json");
        }
        else if (fileName.endsWith(".xml"))
        {
            response.setHeader("Content-Type", "text/xml");
        }
        // Todo: add all of your content types
        else
        {
            qDebug("StaticFileController: unknown MIME type for filename '%s'", qPrintable(fileName));
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    c、下载的源码中有官方的例程可供参考

    五、工程文件下载

    下载点击我

  • 相关阅读:
    【Element-plus】如何让滚动条永远在最底部(支持在线演示)
    SpringMVC--从理解SpringMVC执行流程到SSM框架整合
    【EI会议征稿通知】第四届电网系统与绿色能源国际学术会议(PGSGE 2024)
    Azure 开发者新闻快讯丨开发者7月大事记一览
    react 中DatePicker 使用问题
    如何使用Grid中的repeat函数
    esp32基于IDF配置 coredump时进行gdb调试
    Stable Diffusion 参数介绍及用法
    [答疑]校长出轨主任流程的业务建模
    C++模版初阶
  • 原文地址:https://blog.csdn.net/qq_15181569/article/details/125621359