Qt6.9.1起一个图片服务器(支持前端跨域请求,不支持上传,可扩展)

ImageHttpServer.h

cpp 复制代码
#pragma once

#include <QObject>
#include <QHttpServer>
#include <QTcpServer>

class ImageHttpServer : public QObject
{
    Q_OBJECT

public:
    explicit ImageHttpServer(QObject *parent = nullptr);

    bool start(quint16 port);

private:
    void initImagesDir();
    void setupCors();
    void setupRoutes();

    static bool isIllegalPath(const QString &path);

private:
    QHttpServer m_server;
    QTcpServer  m_tcpServer;
};

ImageHttpServer.cpp

cpp 复制代码
#include "ImageHttpServer.h"

#include <QCoreApplication>
#include <QDir>
#include <QFile>
#include <QMimeDatabase>
#include <QDebug>

ImageHttpServer::ImageHttpServer(QObject *parent)
    : QObject(parent)
{
    initImagesDir();
    setupCors();
    setupRoutes();
}

/*
 * 路径安全校验
 */
bool ImageHttpServer::isIllegalPath(const QString &s)
{
    return s.contains("..") || s.contains('/') || s.contains('\\');
}

/*
 * 初始化 images 目录
 */
void ImageHttpServer::initImagesDir()
{
    const QString imagesDir =
        QCoreApplication::applicationDirPath() + "/images";

    QDir dir;
    if (!dir.exists(imagesDir)) {
        qInfo() << "[初始化] images 目录不存在,正在创建...";
        if (dir.mkpath(imagesDir)) {
            qInfo() << "[初始化] images 目录创建成功:" << imagesDir;
        } else {
            qFatal("[初始化] images 目录创建失败!");
        }
    } else {
        qInfo() << "[初始化] images 目录已存在:" << imagesDir;
    }
}

/*
 * 全局 CORS 设置
 */
void ImageHttpServer::setupCors()
{
    m_server.addAfterRequestHandler(
        this,
        [](const QHttpServerRequest &,
           QHttpServerResponse &response) {

            response.headers().append(
                QByteArray("Access-Control-Allow-Origin"),
                QByteArray("*"));

            response.headers().append(
                QByteArray("Access-Control-Allow-Methods"),
                QByteArray("GET, POST, OPTIONS"));

            response.headers().append(
                QByteArray("Access-Control-Allow-Headers"),
                QByteArray("Content-Type"));

            response.headers().append(
                QByteArray("Access-Control-Allow-Credentials"),
                QByteArray("true"));
        });
}

/*
 * 路由配置
 */
void ImageHttpServer::setupRoutes()
{
    // OPTIONS 预检
    m_server.route(".*",
                   QHttpServerRequest::Method::Options,
                   []() {
                       return QHttpServerResponse(
                           QHttpServerResponse::StatusCode::NoContent);
                   });

    // 图片访问
    m_server.route("/image/<arg>/<arg>",
                   [](const QString &folder,
                      const QString &fileName) {

                       qInfo() << "[HTTP] 图片请求"
                               << folder << fileName;

                       if (isIllegalPath(folder) ||
                           isIllegalPath(fileName)) {
                           return QHttpServerResponse(
                               QHttpServerResponse::StatusCode::Forbidden);
                       }

                       const QString baseDir =
                           QCoreApplication::applicationDirPath() + "/images";
                       const QString fullPath =
                           baseDir + "/" + folder + "/" + fileName;

                       QFile file(fullPath);
                       if (!file.exists()) {
                           return QHttpServerResponse(
                               QHttpServerResponse::StatusCode::NotFound);
                       }

                       if (!file.open(QIODevice::ReadOnly)) {
                           return QHttpServerResponse(
                               QHttpServerResponse::StatusCode::InternalServerError);
                       }

                       QMimeDatabase db;
                       const QByteArray data = file.readAll();
                       const QByteArray mime =
                           db.mimeTypeForFile(fullPath).name().toUtf8();

                       return QHttpServerResponse(mime, data);
                   });
}

/*
 * 启动服务器
 */
bool ImageHttpServer::start(quint16 port)
{
    qInfo() << "[TCP] 监听 0.0.0.0:" << port;

    if (!m_tcpServer.listen(QHostAddress::Any, port)) {
        qCritical() << "[TCP] 监听失败:"
                    << m_tcpServer.errorString();
        return false;
    }

    m_server.bind(&m_tcpServer);

    qInfo() << "[HTTP] 服务启动成功";
    qInfo() << "示例:http://127.0.0.1:"
            << port << "/image/cam1/1.jpg";

    return true;
}

main.cpp

cpp 复制代码
#include <QCoreApplication>
#include <QDebug>
#include "ImageHttpServer.h"

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    qInfo() << "===================================";
    qInfo() << "Qt 图片 HTTP 服务器";
    qInfo() << "Qt 版本:" << QT_VERSION_STR;
    qInfo() << "程序目录:" << QCoreApplication::applicationDirPath();
    qInfo() << "===================================";

    ImageHttpServer server;
    if (!server.start(8080)) {
        return -1;
    }

    return app.exec();
}
相关推荐
Data_Journal3 小时前
Scrapy vs. Crawlee —— 哪个更好?!
运维·人工智能·爬虫·媒体·社媒营销
3 小时前
java关于内部类
java·开发语言
好好沉淀3 小时前
Java 项目中的 .idea 与 target 文件夹
java·开发语言·intellij-idea
只是懒得想了3 小时前
C++实现密码破解工具:从MD5暴力破解到现代哈希安全实践
c++·算法·安全·哈希算法
lsx2024063 小时前
FastAPI 交互式 API 文档
开发语言
VCR__3 小时前
python第三次作业
开发语言·python
YMWM_3 小时前
不同局域网下登录ubuntu主机
linux·运维·ubuntu
码农水水3 小时前
得物Java面试被问:消息队列的死信队列和重试机制
java·开发语言·jvm·数据结构·机器学习·面试·职场和发展
zmjjdank1ng3 小时前
restart与reload的区别
linux·运维
wkd_0073 小时前
【Qt | QTableWidget】QTableWidget 类的详细解析与代码实践
开发语言·qt·qtablewidget·qt5.12.12·qt表格