在实际工作中,为了保证App的高可用性,服务端需要缓存一部分静态资源,通过web服务来分发资源。hv即可快速实现web服务。
hv静态资源服务(如果含中文路径需转utf8,否则抛异常)。
HttpService router;
router.Static("/statics", "smart-yi-ui");
目录结构(smart-yi-ui
映射静态资源):
此时访问:http://127.0.0.1:13456/statics/index.html
即可获取smart-yi-ui
的index.html资源。
hv 支持大文件上传服务
//upload
http_state_handler upload = std::bind(&XHttpServices::OnUpload, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
m_http_router.POST("/shader/upload", upload);
//验签+分段接受存储+应答
int XHttpServices::OnUpload(const HttpContextPtr& ctx, http_parser_state state, const char* data, size_t size)
{
//hv内部会自动urldecode,所以此处不用decode
const std::string& filename = UTF82ASTR(ctx->param("filename"));
const std::string& gamename = UTF82ASTR(ctx->param("gamename"));
const std::string& filemd5 = ctx->param("md5");
int status_code = HTTP_STATUS_UNFINISHED;
HFileUnShared* file = (HFileUnShared*)ctx->userdata;
switch (state) {
case HP_HEADERS_COMPLETE:
{
if (ctx->is(MULTIPART_FORM_DATA))
{
// NOTE: You can use multipart_parser if you want to use multipart/form-data.
ctx->close();
return HTTP_STATUS_BAD_REQUEST;
}
XLOGI("OnUpload url[%s]", ctx->url().c_str());
if (filename.empty() || gamename.empty() || filemd5.empty())
{
response_status(ctx, 1, "params invalid");
return HTTP_STATUS_OK;
}
if (!CheckSign(ctx->params()))
{
response_status(ctx, 2, "sign check error");
XLOGI("OnUpload Skip,game[%s] file[%s] md5[%s] sign not match", gamename.c_str(), filename.c_str(), filemd5.c_str());
return HTTP_STATUS_OK;
}
//查询游戏目录失败
std::string game_dir = QueryGamePath(gamename);
#ifdef _DEBUG
game_dir = "d:\\123\\123";
#endif // _DEBUG
if (game_dir.empty())
{
response_status(ctx, 3, "game dir not find");
XLOGI("OnUpload Skip,game[%s] dir not find", gamename.c_str());
return HTTP_STATUS_OK;
}
ExecludeFolders(game_dir);
std::string save_path =JoinPath(game_dir, SHARDER_FOLDER_NAME);
CreateDir(save_path);
std::string filepath = JoinPath(save_path,filename);
//如果着色文件已经存在则不在上传
if (_access(filepath.c_str(), 00) == 0)
{
response_status(ctx, 0, "file is exist,no need upload");
XLOGI("OnUpload Skip,game[%s] file[%s] md5[%s] file is exist", gamename.c_str(), filename.c_str(), filemd5.c_str());
return HTTP_STATUS_OK;
}
//已独占方式打开
file = new HFileUnShared(filemd5);
int errcode = file->open((filepath+".upload").c_str(), "wb");
if (errcode != 0)
{
XLOGI("OnUpload Skip,open[%s] failed[%d],game[%s] file[%s] md5[%s]", filepath.c_str(),errcode, gamename.c_str(), filename.c_str(), filemd5.c_str());
response_status(ctx, 4, fmt::format("file open failed[{0}]",errcode).c_str());
return HTTP_STATUS_OK;
}
ctx->userdata = file;
}
break;
case HP_BODY:
{
if (file && data && size) {
if (file->write(data, size) != size) {
int errcode = errno;
response_status(ctx, -1, "file write failed");
XLOGI("OnUpload Skip,file[%s] write failed[%d],game[%s] file[%s] md5[%s]", file->filepath, errcode, gamename.c_str(), filename.c_str(), filemd5.c_str());
return HTTP_STATUS_OK;
}
}
}
break;
case HP_MESSAGE_COMPLETE:
{
if (file)
{
if (file->checkmd5())
{
std::string end_suffix = ".upload";
std::string new_filename = file->filepath;
if (hv::endswith(new_filename, end_suffix))
{
new_filename.erase(new_filename.size() - end_suffix.size(), end_suffix.size());
file->rename(new_filename.c_str());
XLOGI("rename [%s]->[%s]", file->filepath, new_filename.c_str());
}
if (_access(new_filename.c_str(), 00) == 0)
{
XLOGI("OnUpload OK,path[%s],game[%s] file[%s] md5[%s]", file->filepath, gamename.c_str(), filename.c_str(), filemd5.c_str());
response_status(ctx, 0, "successed");
}
else
{
XLOGI("OnUpload failed rename failed,path[%s],game[%s] file[%s] md5[%s]", file->filepath, gamename.c_str(), filename.c_str(), filemd5.c_str());
response_status(ctx, -2, "rename failed");
}
}
else
{
XLOGI("OnUpload failed md5 not match,path[%s],game[%s] file[%s] md5[%s]", file->filepath, gamename.c_str(), filename.c_str(), filemd5.c_str());
response_status(ctx, -3, "md5 not match");
}
delete file;
ctx->userdata = NULL;
}
}
break;
case HP_ERROR:
{
if (file)
{
delete file;
ctx->userdata = NULL;
}
}
break;
default:
break;
}
return status_code;
}
int XHttpServices::response_status(HttpResponse* resp, int code, const char* message) {
if (message == NULL) message = http_status_str((enum http_status)code);
resp->Set("code", code);
resp->Set("message", message);
return code;
}
int XHttpServices::response_status(const HttpResponseWriterPtr& writer, int code, const char* message) {
response_status(writer->response.get(), code, message);
writer->End();
return code;
}
int XHttpServices::response_status(const HttpContextPtr& ctx, int code , const char* message ) {
ctx->setContentType(APPLICATION_JSON);
response_status(ctx->response.get(), code, message);
ctx->send();
ctx->close();
return code;
}
hv 实现重定向
http_sync_handler user_status = std::bind(&XHttpServices::OnUserStatus, this, std::placeholders::_1, std::placeholders::_2);
router.GET("/user/status", user_status);
int XHttpServices::OnUserStatus(HttpRequest* req, HttpResponse* resp)
{
//设置重定向地址
resp->SetHeader("Location", "https://www.baidu.com");
return 302;
}
此时访问http://127.0.0.1:13456//user/status
即可重定向到https://www.baidu.com
hv 实现文件下载服务
int XHttpServices::OnDownaload(HttpRequest* req, HttpResponse* resp)
{
//解析请求文件名,匹配文件地址
char* path = "D:\\demo.zip";
return resp->File(path);
}
hv 客户端提交表单
requests::uploadFormFile
如果需要提交多文件,重写uploadFormFile
,SetFormFile支持多文件。