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();
}