700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > C/C++ CGI处理文件上传

C/C++ CGI处理文件上传

时间:2019-04-04 20:13:28

相关推荐

C/C++ CGI处理文件上传

前端页面如下:(index.html)

<!DOCTYPE><html><head><title>Upload File Test</title></head><body><form enctype='multipart/form-data' action="/cgi-bin/LocalVideoUpload" method="post"><input type="file" name='pics' multiple><input type="file" name='another_pic'><input type="submit"></form ></body></html>

从中可以看到,使用了一个表单作为上传内容,其中enctype属性表明这个表单在上传的时候使用multipart/form-data格式。表单中有两个输入框,在浏览器里会显示为【选择文件】。其中pics可以选择多个文件上传,another_pic只能选择一个文件上传。

那么什么是multipart/form-data格式呢?

首先根据标准文档和搜索到的资料,这是一种Content-Type,而且是建立于POST请求方式基础上的。其本身意味着整个表单数据编码为一条消息,每个控件的数据对应消息的一部分。

先用Wireshark抓一下包,这里要注意的是,由于wireshark没法抓localhost环回的包,因此我在本地的另一个设备上设置了一个代理,这样请求会经过该设备的中转,从而能够被wireshark捕获到。当然也有更好的办法,比如配置路由,安装rawpacp等等.... 可以参考这个博客

抓到的包大概是这样:

这里用了wireshark的过滤条件:ip.src eq 192.168.31.102 and ip.dst eq 192.168.31.51 and ip.proto eq TCP,这样能够筛选出从102机器发往51机器的所有TCP报文,沿着SYN查找带有PSH,ACK的报文(其中PSH表示push操作位,意味着发送方希望接收方立刻处理数据,具体参见 这个博客,在截图中还可以看到很多TCP segment of a reassembled PDU这样的提示,这表明TCP报文经过了分片,可以参考 这个博客。关于更多的wireshark过滤条件语法,参见 这个博客)然后右键报文——追踪TCP流,就会弹出来这样一个窗口:

这里就可以看到报文的格式了。在Content-Type中,multipart/form-data被指定,随后跟着一个boundary参数顾名思义就是消息的边界分隔符。在POST的正文中可以看到,每个消息以--{$boundary}开始。最后一个消息后附带一个--{$boundary}--表示结束。整个POST的正文长度(包括一大堆的分隔符和内部消息头)为Content-Length。

这样,在CGI中,接收到CONTENT_TYPE和CONTENT_LENGTH参数后,分析是不是multipart/form-data,如果是就提取出来boundary,读取post内容,根据boundary分割输入数据,解析内部请求头。(此处代码较多,就不贴了)

需要注意的是,如果CGI运行在Windows下,而且上传的文件中包含二进制数据,此时需要将标准输入流设置成二进制模式,具体参见 这个博客。

有关内部消息头中的Content-Disposition,可以参考 这里。另外Content-Disposition还可以被用在响应头中,用于下载文件,具体参考 这个博客。

由于内部消息头没有Content-Length,因此没有办法预知这一部分有多少数据,只能不断读入然后判断是否遇到了边界。

需要小心的是,内部消息头指出的filename可能是经过精心编辑的带有类似 ../ 的路径,如果直接使用有可能会覆盖其他文件,从而为攻击提供了可乘之机。

这里再介绍一种能够显示上传进度的前端页面写法:(摘自 这个博客)

<!DOCTYPE html><html><head><title>Upload Files using XMLHttpRequest - Minimal</title><script type="text/javascript">function fileSelected() {var file = document.getElementById('fileToUpload').files[0];if (file) {var fileSize = 0;if (file.size > 1024 * 1024)fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';elsefileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';document.getElementById('fileName').innerHTML = 'Name: ' + file.name;document.getElementById('fileSize').innerHTML = 'Size: ' + fileSize;document.getElementById('fileType').innerHTML = 'Type: ' + file.type;}}function uploadFile() {var fd = new FormData();fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]);var xhr = new XMLHttpRequest();xhr.upload.addEventListener("progress", uploadProgress, false);xhr.addEventListener("load", uploadComplete, false);xhr.addEventListener("error", uploadFailed, false);xhr.addEventListener("abort", uploadCanceled, false);xhr.open("POST", "/cgi-bin/LocalVideoUpload");//修改成自己的接口xhr.send(fd);}function uploadProgress(evt) {if (evt.lengthComputable) {var percentComplete = Math.round(evt.loaded * 100 / evt.total);document.getElementById('progressNumber').innerHTML = percentComplete.toString() + '%';}else {document.getElementById('progressNumber').innerHTML = 'unable to compute';}}function uploadComplete(evt) {/* 服务器端返回响应时候触发event事件*/alert(evt.target.responseText);}function uploadFailed(evt) {alert("There was an error attempting to upload the file.");}function uploadCanceled(evt) {alert("The upload has been canceled by the user or the browser dropped the connection.");}</script></head><body><form id="form1" enctype="multipart/form-data" method="post"><div class="row"><label for="fileToUpload">Select a File to Upload</label><br /><input type="file" name="fileToUpload" id="fileToUpload" onchange="fileSelected();"/></div><div id="fileName"></div><div id="fileSize"></div><div id="fileType"></div><div class="row"><input type="button" onclick="uploadFile()" value="Upload" /></div><div id="progressNumber"></div></form></body></html>

此外,还可以使用libcurl库和winhttp库来直接对cgi发起上传文件的POST请求,可以参考 这个博客。

POST上传数据的时候,除了multipart/form-data外,还有默认的application/x-www-from-urlencoded类型,这种类型会将数据转化为键值对key=value&key2=value2这样的,同时还会对数据进行编码(不适合文件上传)。还有raw类型,其Content-Type为text/plain或者application/json,text/html这样的。其内容不会被修改。有关更多的Content-Type内容,可以参考 这个博客。除此之外还有一种binary类型,其Content-Type为application/octet-stream,由于没有分隔符,因此只能上传一个文件。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。