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();
}
相关推荐
小鱼仙官12 分钟前
Qt 主界面弹出分界面
开发语言·qt
阿标在干嘛22 分钟前
容器编排的“场景适配”:我们为什么在部分服务中回归了Docker Compose?
运维·docker·容器
你怎么知道我是队长31 分钟前
C++零基础第3周:if/else、switch、for/while/do-while 控制流完全指南(附20段可运行代码+避坑清单)
java·开发语言·c++
冻柠檬飞冰走茶1 小时前
PTA刷题集之Airline Routes(C语言实现)
c语言·开发语言·数据结构·算法
damoluomu1 小时前
Node 的 Headless 出厂没有前台,Java 的 Headless 是从门户里长出来的
java·开发语言·node.js
我命由我123451 小时前
Python 实用操作 - 文件操作(打开与关闭文件、读取文件、文件写入)
java·开发语言·前端·javascript·数据库·python·java-ee
墨理学AI1 小时前
虚拟机导致的服务器交换内容占用过大(服务器卡顿)问题 快速解决
运维·服务器
青少儿编程课堂1 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级:倍增法求最近公共祖先LCA解析
c++·python·算法·bfs·信息学竞赛
冻柠檬飞冰走茶1 小时前
PTA刷题集之Battle Over Cities - Hard Version(C语言实现)
c语言·开发语言·数据结构·算法
程序猿老A1 小时前
轻量应用服务器怎么搭建个人网站?
服务器