multipart-parser-c 使用方式

源码地址

https://github.com/iafonov/multipart-parser-c

API 说明

multipart_parser_init

cpp 复制代码
/**
    用来初始化一个 multipart_parser 结构

    boundary : 需要自行从 http 头中解析,格式为 --<boundary>,需要加上 -- 起始字符
    settings :回调函数设置
**/
multipart_parser* multipart_parser_init
    (const char *boundary, const multipart_parser_settings* settings)

multipart_parser_set_data

cpp 复制代码
/**
    用来设置用户自定义结构到 multipart_parser 上下文中,方便后续使用

    p    : multipart_parser 的上下文结构
    data : 用户自定义结构,用来存放用户数据
**/
void multipart_parser_set_data(multipart_parser *p, void *data)

multipart_parser_get_data

cpp 复制代码
/**
    获取 multipart_parser_set_data 设置的自定义结构
**/
void *multipart_parser_get_data(multipart_parser *p)

multipart_parser_execute

cpp 复制代码
/**
    用来解析数据,可以重复调用,直到将所有数据处理完

    p   : multipart_parser 上下文
    buf : 要解析的数据
    len : 数据长度
**/
size_t multipart_parser_execute(multipart_parser* p, const char *buf, size_t len)

multipart_parser_free

cpp 复制代码
/**
    释放 multipart_parser_init 创建的上下文结构
**/
void multipart_parser_free(multipart_parser* p)

回调函数说明

回调结构如下,multipart_parser_execute 在解析数据时会按顺序调用各个回调函数

cpp 复制代码
typedef int (*multipart_data_cb) (multipart_parser*, const char *at, size_t length);
typedef int (*multipart_notify_cb) (multipart_parser*);

struct multipart_parser_settings {
  multipart_data_cb on_header_field;
  multipart_data_cb on_header_value;
  multipart_data_cb on_part_data;

  multipart_notify_cb on_part_data_begin;
  multipart_notify_cb on_headers_complete;
  multipart_notify_cb on_part_data_end;
  multipart_notify_cb on_body_end;
};

on_part_data_begin

每个新的 multipart 部分开始时调用,包括表单字段和文件上传。此时可以在该回调用中初始化使用 multipart_parser_set_data 设置的用户自定义结构

on_header_field

遇到头部字段名时调用,可能会被多次调用(对于较长的字段名)

on_header_value

头部字段值出现时调用,紧跟在 on_header_field 之后

on_headers_complete

一个部分的所有头部解析完成后调用,在on_part_data之前

on_part_data

处理实际数据部分时调用,可能会被多次调用(数据分块)。如果是文件上传,此时就可以开始写文件了

on_part_data_end

一个 multipart  部分完全解析完成后调用。如果是文件上传,此时可以关闭文件句柄了。

on_body_end

所有数据处理完成后调用。此时可以释放资源了。

示例

一个从服务器接收文件的示例

cpp 复制代码
// 自定义结构
typedef struct
{
    FILE *fp;
    int is_file_type;
    char save_filename[128];
    char current_header[256];
} m_upload_context;

// 开始接收数据前的初始化操作
static int multipart_on_part_begin_cb(multipart_parser *parser)
{
    m_upload_context *ctx = (m_upload_context *)multipart_parser_get_data(parser);

    ctx->fp = NULL;
    ctx->is_file_type = 0;
    bzero(ctx->current_header, sizeof(ctx->current_header));

    return 0;
}

// 保存 header key 值,value值在 on_header_value 中接收处理
static int multipart_on_header_field_cb(multipart_parser *parser, const char *at, size_t length)
{
    m_upload_context *ctx = (m_upload_context *)multipart_parser_get_data(parser);

    snprintf(ctx->current_header, MIN(sizeof(ctx->current_header), length + 1), "%s", at);

    return 0;
}

// 从 Content-Disposition 值中判断该部分数据是不是文件
static int multipart_on_header_value_cb(multipart_parser *parser, const char *at, size_t length)
{
    m_upload_context *ctx = (m_upload_context *)multipart_parser_get_data(parser);

    if (strcasecmp(ctx->current_header, "Content-Disposition") == 0) 
    {
        if (strstr(at, "filename=\""))
        {
            ctx->is_file_type = 1;
        }
    }
    return 0;
}

// 头部解析完成,如果该部分数据是文件内容,则打开文件句柄
static int multipart_on_headers_complete_cb(multipart_parser *parser)
{
    m_upload_context *ctx = (m_upload_context *)multipart_parser_get_data(parser);

    if (ctx->is_file_type == 1)
    {
        ctx->fp = fopen(ctx->save_filename, "wb");
        if (NULL == ctx->fp)
        {
            cgi_log_error("open file %s fail.", ctx->save_filename);
            return -1;
        }
    }
    return 0;
}

// 开始写文件
static int multipart_on_part_data_cb(multipart_parser *parser, const char *at, size_t length)
{
    m_upload_context *ctx = (m_upload_context *)multipart_parser_get_data(parser);

    if (ctx->fp)
    {
        fwrite(at, 1, length, ctx->fp);
    }

    return 0;
}

// 文件写结束,关闭文件句柄
static int multipart_on_part_end_cb(multipart_parser *parser)
{
    m_upload_context *ctx = (m_upload_context *)multipart_parser_get_data(parser);

    if (ctx->fp)
    {
        fclose(ctx->fp);
    }

    return 0;
}

int demo(char *filename)
{
    int read_len = 0;
    int content_len = xxx;         // 报文长度自行获取
    char *boundary = "--xxx";      // boundary 值自行获取
    char read_buff[1024] = {0};

    m_upload_context m_ctx;
    multipart_parser *m_parser = NULL;
    multipart_parser_settings m_setting;

    m_setting.on_part_data_begin = multipart_on_part_begin_cb;
    m_setting.on_header_field = multipart_on_header_field_cb;
    m_setting.on_header_value = multipart_on_header_value_cb;
    m_setting.on_headers_complete = multipart_on_headers_complete_cb;
    m_setting.on_part_data = multipart_on_part_data_cb;
    m_setting.on_part_data_end = multipart_on_part_end_cb;

    snprintf(m_ctx.save_filename, sizeof(m_ctx.save_filename), "%s", filename);

    m_parser = multipart_parser_init(boundary , &m_setting);
    multipart_parser_set_data(m_parser, &m_ctx);

    while (content_len > 0)
    {
        read_len = fread(read_buff, 1, 1024, stdin);
        multipart_parser_execute(m_parser, read_buff, read_len );
        content_len -= read_len;
    }
    multipart_parser_free(m_parser);

    return 0
}
相关推荐
chilavert3182 小时前
技术演进中的开发沉思-260 Ajax:核心动画
开发语言·javascript·ajax
云中飞鸿2 小时前
为什么有out参数存在?
开发语言·c#
飞天遇见妞2 小时前
C/C++中宏定义的使用
c语言·开发语言·c++
雨落在了我的手上2 小时前
C语言入门(三十二):预处理详解(2)
c语言·开发语言
专注API从业者2 小时前
构建企业级 1688 数据管道:商品详情 API 的分布式采集与容错设计
大数据·开发语言·数据结构·数据库·分布式
疏狂难除2 小时前
windows上使用LLVM编译lua
开发语言·lua
沐知全栈开发2 小时前
XML Schema 复合元素 - 仅含文本
开发语言
代码游侠2 小时前
复习——线程(pthread)
linux·运维·开发语言·网络·学习·算法