新增支持http文件上传功能

This commit is contained in:
xiongziliang
2026-07-08 10:45:55 +08:00
parent b44e5b0731
commit 92681fe6e1
10 changed files with 181 additions and 10 deletions

View File

@@ -130,12 +130,17 @@ const string &Parser::content() const {
return _content;
}
const HttpBody::Ptr &Parser::body() const {
return _body;
}
void Parser::clear() {
_method.clear();
_url.clear();
_params.clear();
_protocol.clear();
_content.clear();
_body = nullptr;
_headers.clear();
_url_args.clear();
}
@@ -152,6 +157,10 @@ void Parser::setContent(string content) {
_content = std::move(content);
}
void Parser::setBody(HttpBody::Ptr body) {
_body = std::move(body);
}
StrCaseMap &Parser::getHeader() const {
return _headers;
}

View File

@@ -14,6 +14,7 @@
#include <map>
#include <string>
#include "Util/util.h"
#include "Http/HttpBody.h"
namespace mediakit {
@@ -95,6 +96,7 @@ public:
// 获取http body或sdp [AUTO-TRANSLATED:d6fd1803]
// Get http body or sdp
const std::string &content() const;
const HttpBody::Ptr &body() const;
// 清空,为了重用 [AUTO-TRANSLATED:cb7a16dd]
// Clear, for reuse
@@ -111,6 +113,7 @@ public:
// 重新设置content [AUTO-TRANSLATED:ac8fc8c0]
// Reset content
void setContent(std::string content);
void setBody(HttpBody::Ptr body);
// 获取header列表 [AUTO-TRANSLATED:90d90b03]
// Get header list
@@ -131,6 +134,7 @@ private:
std::string _url;
std::string _protocol;
std::string _content;
HttpBody::Ptr _body;
std::string _params;
mutable StrCaseMap _headers;
mutable StrCaseMap _url_args;

View File

@@ -61,6 +61,7 @@ const string kBroadcastMediaChanged = "kBroadcastMediaChanged";
const string kBroadcastRecordMP4 = "kBroadcastRecordMP4";
const string kBroadcastRecordTs = "kBroadcastRecordTs";
const string kBroadcastHttpRequest = "kBroadcastHttpRequest";
const string kBroadcastBeforeHttpRequest = "kBroadcastBeforeHttpRequest";
const string kBroadcastHttpAccess = "kBroadcastHttpAccess";
const string kBroadcastOnGetRtspRealm = "kBroadcastOnGetRtspRealm";
const string kBroadcastOnRtspAuth = "kBroadcastOnRtspAuth";

View File

@@ -57,6 +57,11 @@ extern const std::string kBroadcastRecordTs;
extern const std::string kBroadcastHttpRequest;
#define BroadcastHttpRequestArgs const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, bool &consumed, toolkit::SockInfo &sender
// 收到http PUT/POST请求body前的广播监听者可以设置body以接管请求体
// Broadcast before receiving http PUT/POST request body, listener can set body to take over the request body
extern const std::string kBroadcastBeforeHttpRequest;
#define BroadcastBeforeHttpRequestArgs const Parser &parser, HttpBody::Ptr &body, HttpSession &sender
// 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 [AUTO-TRANSLATED:2de426b4]
// In the http file server, broadcast for receiving http access to files or directories. Control access permissions to the http directory through this event.
extern const std::string kBroadcastHttpAccess;

View File

@@ -400,4 +400,50 @@ Buffer::Ptr HttpBufferBody::readData(size_t size) {
return Buffer::Ptr(std::move(_buffer));
}
} // namespace mediakit
// ─────────────────────────────────────────────────────────────────────────────
// HttpFileStorage — write-only body backed by a file on disk
// ─────────────────────────────────────────────────────────────────────────────
HttpFileStorage::HttpFileStorage(std::string file_path) {
_fp = fopen(file_path.data(), "ab");
if (!_fp) {
throw std::runtime_error("HttpFileStorage: failed to open file: " + file_path + ", err: " + toolkit::get_uv_errmsg());
}
_path = std::move(file_path);
}
HttpFileStorage::~HttpFileStorage() {
if (_fp) {
fflush(_fp);
fclose(_fp);
_fp = nullptr;
}
}
void HttpFileStorage::writeData(const char *data, size_t size) {
if (!_fp) {
throw std::runtime_error("HttpFileStorage: file not open");
}
if (size == 0) {
return;
}
auto written = fwrite(data, 1, size, _fp);
if (written != size) {
throw std::runtime_error("HttpFileStorage: fwrite failed, expected " + std::to_string(size) + ", got " + std::to_string(written));
}
_written += written;
}
int64_t HttpFileStorage::remainSize() {
return 0;
}
Buffer::Ptr HttpFileStorage::readData(size_t size) {
return nullptr;
}
const std::string &HttpFileStorage::filePath() const {
return _path;
}
} // namespace kule

View File

@@ -33,7 +33,11 @@ namespace mediakit {
class HttpBody : public std::enable_shared_from_this<HttpBody>{
public:
using Ptr = std::shared_ptr<HttpBody>;
virtual ~HttpBody() = default;
virtual ~HttpBody() {
if (_on_completed) {
_on_completed();
}
}
/**
* 剩余数据大小,如果返回-1, 那么就不设置content-length
@@ -88,6 +92,19 @@ public:
virtual int sendFile(int fd) {
return -1;
}
/**
* 写入数据,默认抛出异常表示不支持写入
* Write data, default throws exception indicating write not supported
*/
virtual void writeData(const char *data, size_t size) {
throw std::runtime_error("HttpBody::writeData not supported");
}
void setOnCompleted(std::function<void()> on_completed) { _on_completed = std::move(on_completed); }
private:
std::function<void()> _on_completed;
};
/**
@@ -127,13 +144,21 @@ private:
toolkit::Buffer::Ptr _buffer;
};
class HttpFileBodyBase : public HttpBody {
public:
using Ptr = std::shared_ptr<HttpFileBodyBase>;
virtual void setRange(uint64_t offset, uint64_t max_size) = 0;
virtual ~HttpFileBodyBase() = default;
};
/**
* 文件类型的content
* File type content
* [AUTO-TRANSLATED:baf9c0f3]
*/
class HttpFileBody : public HttpBody {
class HttpFileBody : public HttpFileBodyBase {
public:
using Ptr = std::shared_ptr<HttpFileBody>;
@@ -159,7 +184,7 @@ public:
* [AUTO-TRANSLATED:30532a4e]
*/
void setRange(uint64_t offset, uint64_t max_size);
void setRange(uint64_t offset, uint64_t max_size) override;
int64_t remainSize() override;
toolkit::Buffer::Ptr readData(size_t size) override;
@@ -173,6 +198,32 @@ private:
toolkit::ResourcePool<toolkit::BufferRaw> _pool;
};
/**
* 文件写入类型的content支持通过writeData向文件追加写入
* File storage content, supports appending data to a file via writeData
*/
class HttpFileStorage : public HttpBody {
public:
using Ptr = std::shared_ptr<HttpFileStorage>;
/**
* @param file_path 文件路径,文件以二进制追加模式打开
* @param file_path File path, opened in binary append mode
*/
HttpFileStorage(std::string file_path);
~HttpFileStorage() override;
void writeData(const char *data, size_t size) override;
int64_t remainSize() override;
toolkit::Buffer::Ptr readData(size_t size) override;
const std::string& filePath() const;
private:
FILE *_fp = nullptr;
int64_t _written = 0;
std::string _path;
};
class HttpArgs;
/**
@@ -212,7 +263,7 @@ private:
int64_t _totalSize;
std::string _bodyPrefix;
std::string _bodySuffix;
HttpFileBody::Ptr _fileBody;
HttpFileBodyBase::Ptr _fileBody;
};
}//namespace mediakit

View File

@@ -138,6 +138,7 @@ void HttpRequestSplitter::input(const char *data,size_t len) {
// _content_len < 0; Data is processed according to variable length content
onRecvContent(ptr,_remain_data_size);//消费掉所有剩余数据
_remain_data.clear();
_remain_data_size = 0;
}
void HttpRequestSplitter::setContentLen(ssize_t content_len) {

View File

@@ -109,19 +109,28 @@ ssize_t HttpSession::onRecvHeader(const char *header, size_t len) {
return _on_recv_body ? -1 : 0;
}
if (content_len > _max_req_size) {
HttpBody::Ptr body;
if (_parser.method() == "PUT" || _parser.method() == "POST") {
NOTICE_EMIT(BroadcastBeforeHttpRequestArgs, Broadcast::kBroadcastBeforeHttpRequest, _parser, body, *this);
}
if (content_len > _max_req_size || body) {
// // 不定长body或超大body //// [AUTO-TRANSLATED:8d66ee77]
// // Indefinite length body or oversized body ////
if (content_len != SIZE_MAX) {
if (content_len != SIZE_MAX && !body) {
WarnL << "Http body size is too huge: " << content_len << " > " << _max_req_size
<< ", please set " << Http::kMaxReqSize << " in config.ini file.";
}
size_t received = 0;
auto parser = std::move(_parser);
_on_recv_body = [this, parser, received, content_len](const char *data, size_t len) mutable {
_on_recv_body = [this, received, content_len, body, it](const char *data, size_t len) mutable {
received += len;
onRecvUnlimitedContent(parser, data, len, content_len, received);
if (body) {
body->writeData(data, len);
} else {
onRecvUnlimitedContent(_parser, data, len, content_len, received);
}
if (received < content_len) {
// 还没收满 [AUTO-TRANSLATED:cecc867e]
// Not yet received
@@ -131,6 +140,13 @@ ssize_t HttpSession::onRecvHeader(const char *header, size_t len) {
// 收满了 [AUTO-TRANSLATED:0c9cebd7]
// Received full
setContentLen(0);
if (body) {
_parser.setBody(std::move(body));
(this->*(it->second))();
}
_parser.clear();
return false;
};
// 声明后续都是bodyHttp body在本对象缓冲不通过HttpRequestSplitter保存 [AUTO-TRANSLATED:0012b6c1]