http上传文件 pda java_通过HTTP协议上传文件

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

一、libcurl

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

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

主要代码:

voidcurl_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" <<:endl>

}

curl_formfree(pFormPost);

curl_easy_cleanup(hCurl);

}

curl_global_cleanup();

}

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

头域设置分割符:

d4e672e0a1f145bb4577dbc2f8fbae49.png

MIME部分:

fc702a777042e1e599388091dffbc13f.png

二、WinHTTP

库:winhttp.lib

主要代码:

voidcswuyg_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 <<:endl>

::WinHttpCloseHandle(hRequest);

::WinHttpCloseHandle(hConnect);

::WinHttpCloseHandle(hSession);

}

wireshark抓包:

头域设置:

67d20c04476ab35c052a11b23f056374.png

表单设置:

36419032df0de4cad68f15f1bc2c8f8e.png

三、总结

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

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

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

附上部分资料:

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协议的文档:

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

2015 补充上传文件时的抓包信息:

51a586a83cc9aae80f282c88654c1276.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值