mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2026-07-28 21:14:03 +08:00
新增支持http文件上传功能
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
// 声明后续都是body;Http body在本对象缓冲,不通过HttpRequestSplitter保存 [AUTO-TRANSLATED:0012b6c1]
|
||||
|
||||
Reference in New Issue
Block a user