http 协议大文件下载

        http 协议的响应报文格式包括起始行+头部字段+响应正文,其格式为:

/*严格按照http应答头格式进行填写:如果有正文,需要在头部字段里写明

** 协议版本|空格|状态码|空格|状态码描述|回车|换行
** 头部字段名|冒号|字段值|回车|换行
** 头部字段名|冒号|字段值|回车|换行
** 头部字段名|冒号|字段值|回车|换行
** .....
** 回车|换行
** 响应正文

*/

        响应的正文只能通过http响应消息进行传输,如果是小文件(比如小于1M)传输,则没有什么问题,直接把文件内容加载到正文进行回应即可,但如果是大文件(比如大于10M),不可能把文件内容加载到正文吧,如果更大的文件呢,100M?

        http 协议的 Transfer-Encoding: chunked 传输方式可以解决文件大小未知的情况(大文件我们也可以当作其大小未知来处理),客户端支持 chunked 传输的情况下,会将服务端发送过来的 n 个 chunked 进行重组,最后形成文件,这种传输方式有格式限制,假如出错的话,客户端可能会一直等待(因为它不知道什么时候结束),其格式如下:

/* chunked 传输方式格式

** 协议版本|空格|状态码|空格|状态码描述|回车|换行
** 头部字段名|冒号|字段值|回车|换行
** 头部字段名|冒号|字段值|回车|换行
** 头部字段名|冒号|字段值|回车|换行
** 
** n1大小|换行
** 响应正文
**
** n2大小|换行
** 响应正文
**
** n2大小|换行
** 响应正文
**
** 0|换行
*/

更形象如下:

http 协议大文件下载_第1张图片

需要注意:响应头只需要一条,而 chunked 则可以是多条,看你将文件分为多少块了,响应头必须要有 "Transfer-Encoding: chunked"。

那用c++代码怎么写呢?主要代码片段如下:

//处理大文件,采用分块传输的方式
std::string headers;
headers.append("HTTP/1.1 200 ok").append("\r\n");
response.getHead(headers);
headers.append("Content-Type: application/octet-stream").append("\r\n");
headers.append("Transfer-Encoding: chunked").append("\r\n").append("\r\n");

mg_send(connection, headers.c_str(), headers.length());
tracef("giant file send header: %s\n", headers.c_str());

sendGiantFile(connection, fileName.c_str());

 最主要就是下面这个函数:

void sendGiantFile(struct mg_connection *connection, const char *fileName)
{
    size_t fileLen = 0;
    int readLen = 0;
    char buf[4096] = {0};
    FILE *fp = fopen(fileName, "r");
    fseek(fp, 0, SEEK_END);
    fileLen = ftell(fp);
    rewind(fp);

    while(fileLen > 0)
    {   
        readLen = fread(buf, 1, sizeof(buf), fp);
        mg_send_http_chunk(connection, buf, readLen);
        fileLen -= readLen;
    }

    fclose(fp);

    //最后0结尾
    tracef("send tail 0 chunked\n");
    mg_send_http_chunk(connection, "", 0);
}

当发送响应头之后 ,用一个 while 循环进行文件块的发送,每次发送大小自行决定,这里用到的是mongoose 里的接口 mg_send_http_chunk() 函数,最后发送一个0及\r\n 表示 chunk 传输结束。其实际效果为:

http 协议大文件下载_第2张图片

下载一个 19 M 的文件是可以成功的,再大的文件没有试,因为下载速度有点慢。那下载下来的文件是否有异常呢?我们可以看一下原文件和下载的文件经过 md5 编码是否一样就可以了。

原文件 md5 码:

http 协议大文件下载_第3张图片

 设备上已经有 md5sum 命令,直接使用了。

下载文件的 md5 码:

http 协议大文件下载_第4张图片

windows上下载的,所以把这个文件拷贝到 CentOS 服务上看一下了,这两个文件经过 md5sum 后值是一样的,那我们下载的文件就是正常的。

http 协议大文件下载_第5张图片

你可能感兴趣的:(c/c++,http,c++)