通过HTTP协议上传文件

HTTP是很常见的协议,虽然用得很多,但对细节的了解却是很浅,这回通过向服务端上传文件信息来理解细节。网络库的选择:1、WinHTTP是windows下常用的库;2、CURL是广受喜爱的开源库。对于我来说,libcurl最大的优点是使用方便,可以把注意力更多的集中到业务层上,提高工作效率,避免重造轮子;缺点是略大(MD编译有264KB,MT编译有340KB),不像WinHTTP可以由windows操作系统集成。下边展示如何使用这两种网络库实现表单POST文件。

一、libcurl

库下载:http://curl.haxx.se/download.html

库编译:把include、lib的代码拿出来,使用静态库,设置预定义宏BUILDING_LIBCURL。

测试工程:设置预定义宏CURL_STATICLIB;添加依赖lib:Ws2_32.lib Wldap32.lib。

主要代码

void curl_post_cswuyg()
{
    curl_global_init(CURL_GLOBAL_ALL);
    CURL* hCurl = curl_easy_init();
    if(hCurl != NULL)
    {
        //也许有Expect: 100-continue,去掉它
        curl_slist* pOptionList = NULL;
        pOptionList = curl_slist_append(pOptionList, "Expect:");
        curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, pOptionList);

        curl_httppost* pFormPost = NULL;
        curl_httppost* pLastElem = NULL;
        //上传文件,指定本地文件完整路径
        curl_formadd(&pFormPost, &pLastElem, CURLFORM_COPYNAME, "ufile01", CURLFORM_FILE, "C:\\temp\\upload_test_curl_cswuyg.txt", CURLFORM_CONTENTTYPE, "application/octet-stream", CURLFORM_END);

        //上传自定义文件内容的文件,CURLFORM_BUFFER指定服务端文件名
        //http://curl.haxx.se/libcurl/c/curl_formadd.html
        char* file_info = "file_info——cswuyg";
        int record_length = std::string(file_info).length();
        curl_formadd(&pFormPost, &pLastElem,
            CURLFORM_COPYNAME, "ufile01",
            CURLFORM_BUFFER, "cswuyg_test.txt",
            CURLFORM_BUFFERPTR, file_info,
            CURLFORM_BUFFERLENGTH, record_length,
            CURLFORM_END);
        //不加一个结束的hfs服务端无法写入文件,一般不存在这种问题,这里加入只是为了测试.
        //curl_formadd(&pFormPost, &pLastElem, CURLFORM_COPYNAME, "end", CURLFORM_COPYCONTENTS, "end", CURLFORM_END);
        curl_easy_setopt(hCurl, CURLOPT_HTTPPOST, pFormPost);
        curl_easy_setopt(hCurl, CURLOPT_URL, "http://127.0.0.1/hfs_up/");

        CURLcode res = curl_easy_perform(hCurl);
        if(res != CURLE_OK)
        {
            std::wcout << "Error" << std::endl;
        }
        curl_formfree(pFormPost);
        curl_easy_cleanup(hCurl);
    }

    curl_global_cleanup();
}

 wireshark抓包,127.0.0.1地址无法抓到,网络上有解决方案。示意图:

头域设置分割符:

MIME部分:

 

二、WinHTTP

库:winhttp.lib

主要代码

void cswuyg_winhttp_post()
{
    HINTERNET hSession = ::WinHttpOpen(L"cswuyg post test/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, NULL);
    HINTERNET hConnect = ::WinHttpConnect(hSession, L"127.0.0.1", INTERNET_DEFAULT_HTTP_PORT, 0);
    if (hConnect == NULL)
    {
        int i = ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
        return;
    }
    const wchar_t* lpszAcceptedType[] = {L"*/*", NULL};
    HINTERNET hRequest = ::WinHttpOpenRequest(hConnect, L"POST", L"hfs_up", L"HTTP/1.1", WINHTTP_NO_REFERER, lpszAcceptedType, 0);
    if (hRequest == NULL)
    {
        return;
    }
    DWORD dwTime = 5000;
    ::WinHttpSetOption(hRequest, WINHTTP_OPTION_CONNECT_TIMEOUT, &dwTime, sizeof(DWORD));
    std::wstring strHeader = L"Content-Type: multipart/form-data; boundary=--boundary_cswuygtest\r\n";
    ::WinHttpAddRequestHeaders(hRequest, strHeader.c_str(), strHeader.length(), WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE);

    std::string strMIME = "----boundary_cswuygtest\r\n";
    strMIME += "Content-Disposition: form-data; name=\"ufile01\"; filename=\"upload_test_http_cswuyg.txt\"\r\n";
    strMIME += "Content-Type:application/octet-stream\r\n\r\n";
    strMIME += "1\r\n";
    /////////////////////////////
    //除了这个表单之外,后头还必须要有另外的表单信息,否则hfs服务端那边无法写入文件,这里仅为测试。
    //strMIME += "----boundary_cswuygtest\r\n" ;
    //strMIME += "Content-Disposition: form-data; name=\"name\"\r\n\r\n";
    //strMIME += "temp.txt\r\n";
    /////////////////////////
    strMIME += "----boundary_cswuygtest--\r\n";
    /////////////////////////
    //可以直接写入
    //::WinHttpSendRequest(hRequest, strHeader.c_str(), strHeader.length(), (LPVOID)strMIME.c_str(), strMIME.length(), strMIME.length(), 0);

    //也可以后面分步写入
    ::WinHttpSendRequest(hRequest, NULL, 0, NULL, 0, strMIME.length(), 0);
    DWORD dwWritten = 0;
    while(!strMIME.empty())
    {
        ::WinHttpWriteData(hRequest, strMIME.c_str(), strMIME.length(), &dwWritten);
        if (strMIME.length() > dwWritten)
        {
            strMIME.substr(dwWritten);
        }
        else
        {
            strMIME = "";
        }

    }
    ::WinHttpReceiveResponse(hRequest, NULL);

    char buf[1024] = { 0 };
    DWORD dwToRead = 1024;
    DWORD dwHaveRead = 0;
    ::WinHttpReadData(hRequest, buf, dwToRead, &dwHaveRead);
    std::cout << buf << std::endl;
    ::WinHttpCloseHandle(hRequest);
    ::WinHttpCloseHandle(hConnect);
    ::WinHttpCloseHandle(hSession);
}

 wireshark抓包

头域设置:

表单设置:

 

三、总结

这两个例子用hfs做服务器测试可以发现在上传文件夹中上传了文件,代码都很简单。测试过程中的最重要的是能尽快的反馈结果,所以能自己搭建一个http服务器很重要,hfs工具,非常简单便捷。

  POST表单最重要的是了解表单的格式。首先要在头域增加Content-Type说明,如:“content-type:multipart/from-data; boundary=--cswuyg”。然后http body填充的数据要以MIME格式上传,每部分数据的开头都是由“--”+boundary开始的,结尾以"--"+boundary+"--"结束。

  如果要上传文件,需要跟服务器约定好格式,譬如上边例子约定在content-Disposition中有filename键的则是上传文件信息,该片的content为文件数据,这应该是比较通用的约定。

  本文所在:http://www.cnblogs.com/cswuyg/p/3185164.html

附上部分资料:

MIME:http://baike.baidu.com/view/160611.htm
MIME来源 : Multipurpose Internet Mail Extensions 多用途互联网邮件扩展。服务器会将它们发送的多媒体数据的类型告诉浏览器,而通知手段就是说明该多媒体数据的MIME类型。
GZIP文件:application/x-gzip
任意的二进制数据:application/octet-stream.
普通文本:text/plain

wireshark:

MIME Multipart Media Encapsulation 代表使用表单上传的所有数据

Encapsulated multipart part 代表表单中不同项的数据

Boundary 用来隔开表单中不同部分的数据

每部分数据的开头都是由“--”+boundary开始的。

如果使用wireshark抓包,而http使用的不是默认的80端口,那么需要抓TCP包,然后再强制Decode为http。

HTTP协议的文档:

http://www.ietf.org/rfc/rfc2616

chroimumde breakpad_sender代码中也有post表单上传文件部分,只不过它是用WinInet写的。

时间: 2024-10-07 00:08:42

通过HTTP协议上传文件的相关文章

上传文件multipart form-data boundary 说明

含义 ENCTYPE="multipart/form-data" 说明: 通过 http 协议上传文件 rfc1867协议概述,客户端发送内容构造. 概述                                                                                            在最初的 http 协议中,没有上传文件方面的功能. rfc1867为 http 协议添加了这个功能.客户端的浏览器,如 Microsoft IE, M

文件上传表单 上传文件的细节 文件上传下载和数据库结合

1 文件上传表单   1)上传文件的本质是文本复制的过程   2)技术层面,在Java中一定会用到IO操作,主要以二进制方式读写   3)传统方式下,对于上传文件字段不同的浏览器有着不同的解析方式,例如:     IE6:upfile=c:\aa\bb\a.JPG     非IE6: upfile=a.JPG   4)可以将form以MIME协议的方式将上传文件传递到服务端,服务端以二进制流的方式读写     代码:客户端form enctype="multipart/form-data&quo

libcurl上传文件,添加自定义头

原文  http://www.cnblogs.com/meteoric_cry/p/4285881.html 主题 curl libcurl参数很多,一不小心就容易遇到问题.曾经就遇到过一个很蛋疼的问题:libcurl断点下载>> 这里主要汇总一下,libcurl上传的二种方式: 1.直接上传文件,类似form表单<input type=”file” />,<form enctype=”multipart/form-data”…: 2.上传二进制流: 至于设定自定义头,都是使

libcurl上传文件

libcurl参数很多,一不小心就容易遇到问题.曾经就遇到过一个很蛋疼的问题:libcurl断点下载>>   这里主要汇总一下,libcurl上传的二种方式: 1.直接上传文件,类似form表单<input type="file" />,<form enctype="multipart/form-data"-: 2.上传二进制流:   至于设定自定义头,都是使用一样的方法 struct curl_slist *headers=NULL;

java http工具类和HttpUrlConnection上传文件分析

利用java中的HttpUrlConnection上传文件,我们其实只要知道Http协议上传文件的标准格式.那么就可以用任何一门语言来模拟浏览器上传文件.下面有几篇文章从http协议入手介绍了java中上传文件. Java使用HttpURLConnection上传文件 使用HttpUrlConnection进行post请求上传文件 封装HttpClient4.3.x包括文件上传 使用 HttpClient 4 进行文件上传 httpclient4教程 下面分享一个自己封装的http工具类(暂不直

如何使用multipart/form-data格式上传文件(POST请求时,数据是放在请求体内,而不是请求头内,在html协议中,用 “\r\n” 换行,而不是 “\n”)

在网络编程过程中需要向服务器上传文件.Multipart/form-data是上传文件的一种方式. Multipart/form-data其实就是浏览器用表单上传文件的方式.最常见的情境是:在写邮件时,向邮件后添加附件,附件通常使用表单添加,也就是用multipart/form-data格式上传到服务器. 表单形式上传附件 具体的步骤是怎样的呢? 首先,客户端和服务器建立连接(TCP协议). 第二,客户端可以向服务器端发送数据.因为上传文件实质上也是向服务器端发送请求. 第三,客户端按照符合“m

Android中利用HTTP协议实现上传文件到服务器

首先我们需要使用HTTP协议发送数据,我们就要知道HTTP发送上传文件到服务器的时候需要哪些头字段已经相关的配置,请看下图 这是使用浏览器模拟上传文件到服务器时候所发送的请求,我们可以看到它包含了请求头字段和实体部分,但是多了一个---------------------------7da2137580612,它实际上是一条分隔线,用于分隔实体数据的,他在使用分隔实体数据的时候会在前面包含多两个"-"而在结束的时候会在除了在前面都出两个减号"-"之外,还会在末尾都出

Android通过HTTP协议实现上传文件数据

SocketHttpRequester.java package cn.itcast.utils; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import

iOS上传文件,有关http上传协议-RFC1867

以上是抓包HTML input file标签上传的内容 只要模拟上面http 的header跟body就可以成功. 整体说明: post 上传文件时,以在http body里面带参数,参数的格式,根据RFC1867协议要求写成如下 在http header 里面定义 boundary ,boundary的值为body里面分隔参数的标志. Content-Type: multipart/form-data; boundary=----------LlcUploadTaskWithRequest 假