谭谭心怎么建设网站,设计公司logo软件,上海企业网站建设公司哪家好,江苏专业做网站的公司有哪些上一篇文章讲到 http 的 MIME 类型 http MIME 类型 里有一个 multipart 多部分对象集合类型#xff0c;这个类型 http 指南里有讲到#xff1a;MIME 中的 multipart#xff08;多部分#xff09;电子邮件报文中包含多个报文#xff0c;它们合在一起作为单一的复杂报文发送… 上一篇文章讲到 http 的 MIME 类型 http MIME 类型 里有一个 multipart 多部分对象集合类型这个类型 http 指南里有讲到MIME 中的 multipart多部分电子邮件报文中包含多个报文它们合在一起作为单一的复杂报文发送。每一部分都是独立的有各自的描述及内容的集不同的部分之间用分界字符串连接在一起。HTTP 也支持多部分主体不过通常只用在下列两种情形之一提交填写好的表格或是作为承载若干文档片段的范围响应。 前端技术不懂这里只用 postman 用为客户端来做示例。表格和文档形式是不是像这样的呢 当你选中其中一种情形时http 的 Headers 里就会多出一个 Content-Type 表明这是一个多部分集合的类型报文 表格情形还没试验过这里主要讲文档情形的。所以呢用客户端 postman 可以向服务端发送大的或是小的文档。下面给出服务端的例子
#include stdio.h
#include stdlib.h
#include unistd.h
#include sys/stat.h
#include string
#include map
#include mutex
#include sstream
#include iostream
#include mongoose.h
#include ../logFormatPrt/log.h#define FILE_NAME_LEN 128
#define FILE_PATH_LEN 32void eventHandler(struct mg_connection *nc, int event, void *eventData);
void fileUpload(mg_connection* nc, const int ev, void* data);
bool validPath(const char *path);struct userData
{int index;
};struct FileInfo
{FILE *fp; //打开新文件的指针char fileName[FILE_NAME_LEN]; //文件名包含路径char filePath[FILE_PATH_LEN]; //文件路径size_t size; //文件大小暂时没有用到size_t byteWrite;//已经写的字节数
};//用postman 测试linux需要关闭防火墙否则收不到数据
int main(int argc, char *argv[])
{ struct mg_mgr mgr;mg_mgr_init(mgr, nullptr);int port 8190;char buf[5] {0};snprintf(buf, sizeof(buf), %d, port);struct mg_connection *con mg_bind(mgr, buf, nullptr);if(con NULL) {errorf(mg_bind fail\n);return -1;}mg_set_protocol_http_websocket(con);infof(listen ip[%s], port[%d]....\n, inet_ntoa(con-sa.sin.sin_addr), port); //uri是/fileUpload 时调用函数fileUploadmg_register_http_endpoint(con, /fileUpload, fileUpload);while (1){mg_mgr_poll(mgr, 100);}mg_mgr_free(mgr);return 0;
}//触发的事件依次为
//#define MG_EV_HTTP_MULTIPART_REQUEST 121 /* struct http_message */
//#define MG_EV_HTTP_PART_BEGIN 122 /* struct mg_http_multipart_part */
//#define MG_EV_HTTP_PART_DATA 123 /* struct mg_http_multipart_part */
//#define MG_EV_HTTP_PART_END 124 /* struct mg_http_multipart_part */
/* struct mg_http_multipart_part */
//#define MG_EV_HTTP_MULTIPART_REQUEST_END 125void fileUpload(mg_connection* nc, const int ev, void* data)
{//用户指针用于保存文件大小文件名struct FileInfo *userData nullptr;//当事件ev是 MG_EV_HTTP_MULTIPART_REQUEST 时data类型是http_messagestruct http_message *httpMsg nullptr;if(MG_EV_HTTP_MULTIPART_REQUEST ev){httpMsg (struct http_message*)data;//初次请求时申请内存if(userData nullptr){userData (struct FileInfo *)malloc(sizeof(struct FileInfo));memset(userData, 0, sizeof(struct FileInfo));}}else // 已经不是第一次请求了nc-user_data 先前已经指向 userData所以可以用了{userData (struct FileInfo *)nc-user_data;}//当事件ev是 MG_EV_HTTP_PART_BEGIN/MG_EV_HTTP_PART_DATA/MG_EV_HTTP_PART_END 时data类型是mg_http_multipart_partstruct mg_http_multipart_part *httpMulMsg nullptr;if(ev MG_EV_HTTP_PART_BEGIN ev MG_EV_HTTP_PART_END){httpMulMsg (struct mg_http_multipart_part*)data;}switch(ev) {case MG_EV_HTTP_MULTIPART_REQUEST:{ ///query_string 为请求地址中的变量, key 名称约定好char filePath[32] {0};std::string key(filePath);//从请求地址里获取 key 对应的值所以这个需要和请求地址里的 key 一样//这里从地址中获取文件要上传到哪个路径if(mg_get_http_var(httpMsg-query_string, key.c_str(), filePath, sizeof(filePath)) 0) {tracef(upload file request, locate: %s %s\n, key.c_str(), filePath); }if(!validPath(filePath)){tracef(no such directory of %s\n, filePath);std::string header;std::string body(no suce directory);header.append(HTTP/1.1 500 file fail).append(\r\n);header.append(Connection: close).append(\r\n);header.append(Content-Length: ).append(std::to_string(body.length())).append(\r\n).append(\r\n);header.append(body).append(\r\n);mg_send(nc, header.c_str(), header.length());nc-flags | MG_F_SEND_AND_CLOSE; }//保存路径且 nc-user_data 指向该内存下次请求就可以直接用了if(userData ! nullptr){snprintf(userData-filePath, sizeof(userData-filePath), %s, filePath);nc-user_data (void *)userData; }}break;case MG_EV_HTTP_PART_BEGIN: ///这一步获取文件名tracef(upload file begin!\n);if(httpMulMsg-file_name ! NULL strlen(httpMulMsg-file_name) 0){tracef(input fileName %s\n, httpMulMsg-file_name);//保存文件名且新建一个文件支持目录带 / 及不带 /if(userData ! nullptr){if(userData-filePath[strlen(userData-filePath)] /){snprintf(userData-fileName, sizeof(userData-fileName), %s%s, userData-filePath, httpMulMsg-file_name);}else{snprintf(userData-fileName, sizeof(userData-fileName), %s/%s, userData-filePath, httpMulMsg-file_name);}userData-fp fopen(userData-fileName, wb);//创建文件失败回复释放内存if(userData-fp NULL) {mg_printf(nc, %s, HTTP/1.1 500 file fail\r\nContent-Length: 25\r\nConnection: close\r\n\r\nFailed to open a file\r\n);nc-flags | MG_F_SEND_AND_CLOSE;free(userData);nc-user_data nullptr; return;} }}break;case MG_EV_HTTP_PART_DATA: //这一步写文件//tracef(upload file chunk size %lu\n, httpMulMsg-data.len);if(userData ! nullptr userData-fp ! NULL) {size_t ret fwrite(httpMulMsg-data.p, 1, httpMulMsg-data.len, userData-fp);if(ret ! httpMulMsg-data.len){mg_printf(nc, %s,HTTP/1.1 500 write fail\r\nContent-Length: 29\r\n\r\nFailed to write to a file\r\n);nc-flags | MG_F_SEND_AND_CLOSE;return;}userData-byteWrite ret; }break;case MG_EV_HTTP_PART_END:tracef(file transfer end!\n);if(userData ! NULL userData-fp ! NULL){mg_printf(nc,HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nWritten %lu bytes of POST data to a file\n\n,userData-byteWrite);//设置标志发送完成数据如果有并且关闭连接nc-flags | MG_F_SEND_AND_CLOSE;//关闭文件释放内存fclose(userData-fp);tracef(upload file end, free userData(%p)\n, userData);free(userData);nc-user_data NULL; } else{mg_printf(nc,HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nWritten 0 of POST data to a file\n\n); } break;case MG_EV_HTTP_MULTIPART_REQUEST_END:tracef(http multipart request end!\n);break;default:break;}
}bool validPath(const char *path)
{struct stat st;if(lstat(path, st) 0){return true;}return false;
}
如果想要直接编译则需要把头文件 #include ../logFormatPrt/log.h 去掉编译选项还得加上 -lssl -lcryptoMakefile 如下
#中间文件存放目录如.o 和 .d 文件
COMPILE_DIR compile
BIN_DIR bin# 可编译arm版本
#CROSS arm-himix200-linux-
CC gcc -m32
CPP $(CROSS)g -stdc11 -m32
CFLAGS -Werror -gLIB -lpthread -lssl -lcrypto
#CPP_SRCS $(wildcard *.cpp)
CPP_SRCS $(shell ls -t | grep \.cpp$$ | head -1)
CPP_OBJS $(patsubst %.cpp, $(COMPILE_DIR)/%.o, $(CPP_SRCS))
CPP_DEP $(patsubst %.cpp, $(COMPILE_DIR)/%.cpp.d, $(CPP_SRCS))C_SRCS mongoose.c
C_OBJS $(patsubst %.c, $(COMPILE_DIR)/%.o, $(C_SRCS))
C_DEP $(patsubst %.c, $(COMPILE_DIR)/%.c.d, $(C_SRCS))OBJS $(CPP_OBJS) $(C_OBJS)
DEP_ALL $(CPP_DEP) $(C_DEP)$(shell if [ ! -d $(COMPILE_DIR) ]; then mkdir $(COMPILE_DIR); fi)
$(shell if [ ! -d $(BIN_DIR) ]; then mkdir $(BIN_DIR); fi)BIN
ifeq ($(target), ) #如果是空的
BIN a.out
else
BIN : $(target)
endifTARGET$(BIN_DIR)/$(BIN)all: $(TARGET)-include $(DEP_ALL)$(TARGET): $(OBJS)$(CPP) $(CFLAGS) $^ -o $ $(LIB)$(COMPILE_DIR)/%.o: %.cpp $(COMPILE_DIR)/%.cpp.d$(CPP) $(CFLAGS) -c $ -o $$(COMPILE_DIR)/%.cpp.d: %.cpp$(CPP) $(CFLAGS) -MM -E -c $ -o $sed s/.*\.o/$(subst /,\/,$(dir $))/g $ $.tmpmv $.tmp $$(COMPILE_DIR)/%.o: %.c $(COMPILE_DIR)/%.c.d$(CC) $(CFLAGS) -c $ -o $$(COMPILE_DIR)/%.c.d: %.c$(CC) $(CFLAGS) -MM -E -c $ -o $sed s/.*\.o/$(subst /,\/,$(dir $))/g $ $.tmpmv $.tmp $.PHONY: clean
clean:rm -rf $(COMPILE_DIR) $(BIN_DIR)大到一个G的文件上传也是没有问题的1.3G 的文件 如果问我客户端怎么写这个我是不会的但 postman 里有个“代码片段”选项可以翻译成不同的编码语言的代码 而在 Mongoose.c 源码里是这样处理 multipart 类型的报文的判断头部字段 Content-Type 为 multipart 时交由相关处理函数进行处理然后就 return 了不再后续处理了。