mongoose库实现http文件上传

这篇具有很好参考价值的文章主要介绍了mongoose库实现http文件上传。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 预备知识

1.1 URL编解码

常用于url链接和application/x-www-form-urlencoded格式的请求体中对参数进行编码
由于url的参数的样子是key1=value1&key2=value2,如果key或者value中包含= &等字符,就会导致解析时混乱了,因此需要一种编码来把这些可能引起歧义的符号替换掉
例如:http://localhost/src/components/global/Checkbox.vue?type=style&index=0
这个链接中 ? 的后面就是参数部分,即 type=style&index=0
这是两个键值对,type值为style,index值为0
假如现在 type 的值为 a=b,那么参数部分最后组装成 type=a=b&index=0 ,可见已经有点歧义了,但由于&分割,兴许还能解析
如果再假设type的值为 a&c=d,那组装后是 type=a&c=d&index=0,显然这个字符串给程序去解析的话,天王老子来了也会被解析为三个部分:type值为a,c值为d,index值为0

1.2 请求体编码格式

Http协议中,请求体有多种格式,如:

  • application/x-www-form-urlencoded,相当常用的格式,即和url中的参数一样,是key=value格式的字符串,且这个字符串是经过url编码的,在解析之前需要进行url解码。
  • multipart/form-data,可以上传多个键值对/文件。具体格式下文将着重展开。
  • application/json,顾名思义,就是json格式的
  • application/xml,xml格式,即像HTML一般的标签
  • text/plain,文本
  • application/octet-stream,二进制数据,且仅能上传一个文件。如果传单个文件(图片、音视频)使用这个相当快乐,它并不需要解析,整个请求体就是文件,但需要使用其他方式上传文件的文件名等信息。

如果有请求体,则应该在请求头使用 Content-Type 说明使用的编码格式

1.3 form-data格式

如果请求体是form-data格式,则在请求头中,我们应该能找到 Content-Type 的值为 multipart/form-data 且它后面会带一个 boundary:

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

这里boundary是解析请求体用的
我们先来看看form-data格式的请求体的样子:

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="myfile"; filename="hello.gif" filename*=UTF-8''hello.gif
Content-Type: image/gif


{二进制数据}
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="mytext"

coolight
------WebKitFormBoundary7MA4YWxkTrZu0gW--

这个请求体示例中有两个部分:

  • 文件名为hello.gif的动态图
  • 一个键值对,键为mytext,值为coolight

刚刚在请求头的 Content-Type 中的boundary的值在请求体中是用来分割数据的,在boundary前加两个-,即:–{boundary},并用它独占一行作为分隔标志。
注意最后一行分割标志的后面还有两个-,即 --{boundary}–
我们先看示例的第二部分,即键值对 mytext=coolight
其格式如下,其中把换行(\r\n)标出:
注意换行 \r\n 也是格式的一部分

--{boundary}(\r\n)
Content-Disposition: form-data; name="{key}"(\r\n)
(\r\n)
{value}(\r\n)
--{boundary}

然后是第一部分,上传文件时的格式:
其中与键值对格式不同的是,在name后面多了个filename,再后面还有一个可选的filename*。这是因为如果filename里面包含中文等非ASCII字符时,因客户端和服务端的编码不同而导致解析时filename乱码,因此可能会多传一个filename*,指定使用的编码格式,如UTF-8,且注意它的值是编码方式后紧接着两个单引号’,然后直接是对应编码的filename,整个字符串两端没有双引号。
注意 行Content-Type 之后还有两行才是数据。

--{boundary}(\r\n)
Content-Disposition: form-data; name="{key}"; filename="{filename}"(\r\n)[; filename*={编码方式}''{对应编码的filename}]
Content-Type: {文件格式}(\r\n)
(\r\n)
(\r\n)
{二进制数据}(\r\n)
--{boundary}

2. mongoose 文件上传实现

由于mongoose并没有提供文件上传功能,因此需要我们自己构建文件上传过程

2.1 构建请求header

header至少包含以下3部分

  • “POST /upload HTTP/1.1\r\n” 说明是post 文件upload
  • “Content-Type: multipart/form-data; Boundary=” 可包含多个form-data
  • "Content-Length: "文件长度=文件头+文件长度+文件尾

2.2 构建body头

  • boundary: 加入一行分割表示一个form-data的开始
  • Content-Disposition: 需指定类型form-data, name是file及filename
  • Content-Type: 参考请求体编码格式,比如上传时jpg图片,那么这里就时image/jpeg

2.3 发送header和body头

2.4 循环发送文件流

2.5 发送body结束分割

单独一行boundary表示一个form-data的结束

3. 完整代码

#include <iostream>
#include "mongoose.h"
#include <string>

static const uint64_t s_timeout_ms = 1500;

static void ev_handler(struct mg_connection* conn, int ev, void* ev_data, void *fn_data) {
    if (ev == MG_EV_OPEN) {
        // Connection created. Store connect expiration time in c->data
        *(uint64_t*)conn->data = mg_millis() + s_timeout_ms;
    }
    else if (ev == MG_EV_POLL) {
        if (mg_millis() > *(uint64_t*)conn->data &&
            (conn->is_connecting || conn->is_resolving)) {
            mg_error(conn, "Connect timeout");
        }
    }
    else if (ev == MG_EV_CONNECT) {
        // Connected to server. Extract host name from URL
    }
    else if (ev == MG_EV_HTTP_MSG) {
        struct mg_http_message* hm = (struct mg_http_message*)ev_data;
        // 处理HTTP响应
        std::cout << "Response body: \n" << hm->body.ptr << std::endl;
        bool* done = (bool*)fn_data;
        *done = true;
    }
}
int main() {
    std::string url {"http://192.168.31.86:8081/upload"};

    // 读取要上传的文件
    FILE* fp = fopen("E:\\code\\Yolov5_Tensorrt_Win10-master\\pictures\\bus.jpg", "rb");
    if (fp == NULL) {
        std::cout << "Failed to open file" << std::endl;
        return 1;
    }
    //get file len
    fseek(fp, 0, SEEK_END);
    int file_size = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    struct mg_mgr mgr;
    struct mg_connection* conn;
    mg_mgr_init(&mgr);

    // create http connection
    bool done = false;
    conn = mg_http_connect(&mgr, url.c_str(), ev_handler, &done);
    if (conn == NULL) {
        std::cout << "Failed to connect" << std::endl;
        return 1;
    }

    // Build HTTP request body
    std::string boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";
    std::string body_header = "--" + boundary + "\r\n"
        "Content-Disposition: form-data; name=\"file\"; filename=\"bus.jpg\"\r\n"
        "Content-Type: image/jpeg\r\n\r\n";

    std::string body_end = "\r\n--" + boundary + "--\r\n";

    // Build HTTP headers
    struct mg_str host = mg_url_host(url.c_str());

    std::string headers = "POST /upload HTTP/1.1\r\n"
        "Host: "+ std::string(host.ptr) + "\r\n"
        "Connection: keep-alive\r\n"
        "Content-Type: multipart/form-data; Boundary=" + boundary + "\r\n"
        "Content-Length: " + std::to_string(body_header.length() + file_size + body_end.length()) + "\r\n\r\n";

    // Send HTTP request
    mg_send(conn, headers.c_str(), headers.length());
    mg_send(conn, body_header.c_str(), body_header.length());

    // 逐块读取文件并发送数据
    char buffer[1024] = { 0 };
    size_t bytes_read = 0;
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
        mg_send(conn, buffer, bytes_read);
    }
    mg_send(conn, body_end.c_str(), body_end.length());

    fclose(fp);

    // 等待响应
    while (!done) {
        mg_mgr_poll(&mgr, 1000);
    }
    mg_mgr_free(&mgr);
    
    return 0;
}

预备知识参考链接:
https://blog.coolight.cool/http-cform-data%E8%A7%A3%E6%9E%90/?replytocom=41295文章来源地址https://www.toymoban.com/news/detail-629462.html

到了这里,关于mongoose库实现http文件上传的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【计算机网络】HTTP协议以及简单的HTTP服务器实现

    虽然我们说, 应用层协议是我们程序猿自己定的. 但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用. HTTP(超文本传输议)就是其中之一。 平时我们俗称的 “网址” 其实就是说的 URL 像 / ? : 等这样的字符, 已经被url当做特殊意义理解了.

    2024年01月20日
    浏览(46)
  • Mongoose http server 例子

            今天抽了点时间看了一下 mongoose的源码, github 地址,发现跟以前公司内部使用的不太一样,这里正好利用其 http server 例子来看一下。以前的 http message 结构体是这样的: github 上的源码的http message 结构体是这样的: 很明显现在的头部消息使用了 mg_http_header 结构体,

    2024年02月09日
    浏览(27)
  • Go语言使用net/http实现简单登录验证和文件上传功能

         最近再看Go语言web编程,go语言搭建Web服务器,既可以用go原生的net/http包,也可以用gin/fasthttp/fiber等这些Web框架。本博客使用net/http模块编写了一个简单的登录验证和文件上传的功能,在此做个简单记录。 目录 1.文件目录结构 2.编译运行 3.用户登录  4.文件上传 5.mime/m

    2024年02月11日
    浏览(34)
  • JS-27 前端数据请求方式;HTTP协议的解析;JavaScript XHR、Fetch的数据请求与响应函数;前端文件上传XHR、Fetch;安装浏览器插件FeHelper

    早期的网页都是通过后端渲染来完成的,即服务器端渲染(SSR,server side render): 客户端发出请求 - 服务端接收请求并返回相应HTML文档 - 页面刷新,客户端加载新的HTML文档; 服务器端渲染的缺点: 当用户点击页面中的某个按钮向服务器发送请求时,页面本质上只是一些数

    2024年02月16日
    浏览(48)
  • 【Linux命令详解 | wget命令】 wget命令用于从网络下载文件,支持HTTP、HTTPS和FTP协议

    在编程世界中,处理网络资源是一项关键任务,而 wget 命令就是一位可靠的助手。 wget (全名为“Web Get”)是一种用于从网络下载文件的工具,它能够处理多种协议,包括HTTP、HTTPS和FTP。作为一个博主,我们可以利用 wget 命令来获取文件、备份网站内容、自动下载资源等。

    2024年02月12日
    浏览(34)
  • el-upload实现自定义携带参数上传文件( :http-request 方式)

    1. el-upload组件 使用 :http-request 自定义上传方法,action仍然要有,随便起个名字即可, 注意使用 :http-request 之后, :on-success, :on-error 指令是不会触发的 自定义上传 函数为  uploadFile 2. 封装上传方法(定义传输请求头,传输格式) 在main.js中将封装好的方法加入全局,后面可直接

    2024年02月11日
    浏览(35)
  • 计算机网络 应用层上 | 域名解析系统DNS 文件传输协议FTP,NFS 万维网URL HTTP HTML

    之前我们讲运输层的时候已经讲了运输层可以给不同进程之间通信,但我们还需要应用层原因是,许多 应用需要多个进程之间相互配合完成,所以应用层进程用来约束这些配合! 每个应用层协议用来解决一个问题 应用层的许多协议都是基于客户服务器方式 客户是请求方,服

    2024年01月24日
    浏览(48)
  • [micropython k210] 基于 Socket 实现 MicroPython 的 HTTP 上传文件(multipart/form-data)

    下述内容需要具备 HTTP 的基础知识,如果不知道的可以过一遍 HTTP 协议详解 继上次在 K210 实现 HTTP Download 文件(https 也支持辣),现在就来说说直接基于 socket 的上传文件实现吧。 首先准备一个 Server 文件服务器的 CPython 代码,这个是再简单不过了。 可以看到实现处理了一个

    2024年02月08日
    浏览(41)
  • SIM800C连接OneNet平台HTTP协议上传GPS数据

    科技进步不断推动新产品的出现,越来越多的电子产品开始着力解决社会焦点问题,随着城市发展以及人口流动性的增加,儿童户外被拐,老人外出走丢,交通工具被盗和大型物件丢失等问题变样严重起来,因此,为了更好地利用现代科学技术解决社会关注的问题。本文就设计出一款基

    2024年02月02日
    浏览(38)
  • 【网络协议】聊聊http协议

    当我们输入www.baidu.com的时候,其实是先将baidu.com的域名进行DNS解析,转换成对应的ip地址,然后开始进行基于TCP构建三次握手的连接,目前使用的是1.1 默认是开启了keep-Alive。可以在多次请求中进行连接复用。 连接建立之后,就要发送HTTP的请求了, 整体其实就是三部分,请

    2024年02月06日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包