编程实战:自己编写HTTP服务器(系列9:上传文件)

初级代码游戏的专栏介绍与文章目录-CSDN博客

我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。

这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。

本系列的源码位于httpd目录下。系列入口:

编程实战:自己编写HTTP服务器(系列1:概述和应答)-CSDN博客


        通过HTTP上传文件需要服务端支持,html虽然支持类型为“file”的input,能选择文件,但是服务器还必须正确支持才行,毕竟如果写一个html就能往服务器上放文件可就太乱了。

目录

一、html和http的file支持

二、服务端代码

2.1 服务端入口

2.2 实际功能

三、更多讨论


一、html和http的file支持

        一个上传文件的form很简单:

<form id="upload-form" action="/Upload.asp" method="post" enctype="multipart/form-data" ><input type="file" id="upload" name="upload" /><input type="submit" value="Upload" />
</form>

        确实很简单,最重要的秘密是必须编码为“multipart/form-data”,当然也必须用POST。

        “multipart/form-data”的格式也很简单:

multipart/form-data; boundary=ASDFGHJKL--ASDFGHJKL
Content-Disposition: form-data; name="upload"; filename="文件名"
Content-Type: text/plain文件内容
--ASDFGHJKL--

        注意一下格式细节,其中片段分隔符boundary是“ASDFGHJKL”,实际会更长一些,并且可能以几个“-”打头,容易和后面引导的“--”混淆。

        片段以“--”加分隔符开始,是一行,按照http规则,以“\r\n”为行结束。

        然后跟两个头标,格式和http头标完全相同。

        再后面是一个空行表示头标部分结束。

        投标部分的后面就是数据,没有数据长度,要根据结束标志判断,结束标志是“--”加分隔符再加“--”。这个例子中我上传的是txt文件,所以所以内容类型为text/plain,如果上传的是rar文件,则类型为字节流。

        文件内容和结束标志之间一定有“\r\n”分隔,我上传的文本文件最后没有换行,但结束标志前增加了“\r\n”,如果文件最后有换行,会怎么样我没有验证(我猜还是追加换行然后是结束标志)。

二、服务端代码

2.1 服务端入口

        我将上传功能做成了内置页面,页面地址为“/Upload.asp”,主代码中添了这么一段:

				else if ("/Upload.asp" == m_request.GetResource()){if (doPageUpload(&connectdata)){m_respond.Flush(m_s);}else{m_respond.Flush(m_s);m_s.Close();//所有此类页面都可能无法预先确定输出长度isKeepAlive = false;}}

        具体位置在myhttpserver.h的SocketProcess函数中。

2.2 实际功能

        实际功能位于doPageUpload函数:

		bool doPageUpload(CConnectData* pCD){CHttpRequest& m_request = pCD->m_request;//请求CHttpRespond& m_respond = pCD->m_respond;//应答CMySocket& m_s = pCD->m_s;//连接m_respond.Init();m_respond.AddHeaderNoCache();m_respond.AddHeaderContentTypeByFilename("*.html");m_respond.AppendBodyHtmlStart("Uplaod");m_respond.AppendBody(m_request.RequestHtmlReport());m_respond.AppendBody("<HR/>");m_respond.AppendBody(m_request.GetFullRequest());m_respond.AppendBody("<HR/>");m_respond.AppendBody(m_request.GetContent().data());m_respond.AppendBody("<HR/>");string content_type = m_request.GetContentType();string a = "multipart/form-data; boundary=";string boundary;size_t pos = content_type.find(a);if (0 != pos){m_respond.AppendBody("未识别的内容类型,仅支持 multipart/form-data<P>");}else{boundary = content_type.substr(a.size());string boundary_begin = "--" + boundary + "\r\n";string boundary_end = "--" + boundary + "--\r\n";size_t part_pos;size_t pos_next_find = 0;while (CBuffer::npos != (part_pos = m_request.GetContent().find(boundary_begin, pos_next_find))){size_t part_end = m_request.GetContent().find(boundary_end, part_pos + boundary_begin.size());if (CBuffer::npos == part_end){m_respond.AppendBody("数据内容不完整<P>");break;}size_t part_header_end = m_request.GetContent().find("\r\n\r\n");string part_header = m_request.GetContent().substr(part_pos + boundary_begin.size(), part_header_end - part_pos - boundary_begin.size());size_t pos_file_name_begin;string filename_head="filename=\"";pos_file_name_begin = part_header.find(filename_head);if (string::npos == pos_file_name_begin){m_respond.AppendBody("没有文件名<P>");}else{size_t pos_file_name_end = part_header.find("\"", pos_file_name_begin + filename_head.size());if (string::npos == pos_file_name_end){m_respond.AppendBody("文件名格式问题<P>");}else{//thelog << pos_file_name_begin << " " << pos_file_name_end << " " << pos_file_name_end - pos_file_name_begin - filename_head.size() << endi;string filename = part_header.substr(pos_file_name_begin + filename_head.size(), pos_file_name_end - pos_file_name_begin - filename_head.size());if (filename.size() == 0){m_respond.AppendBody("没有文件名<P>");}else{m_respond.AppendBody(filename);CEasyFile file;size_t pos_filedata = part_header_end + 4;long filesize = part_end - pos_filedata - 2;//前面还有\r\nif (!file.WriteFile(filename.c_str(), m_request.GetContent().data() + pos_filedata, filesize)){m_respond.AppendBody("写入失败<P>");}else{char buf[256];sprintf(buf, "写入成功 字节数%ld<P>", filesize);m_respond.AppendBody(buf);char path[1024];m_respond.AppendBody(getcwd(path, 1024));}}}}pos_next_find = part_end + boundary_end.size();m_respond.AppendBody("<HR />");}}string form = "<form id=\"upload-form\" action=\"/Upload.asp\" method=\"post\" enctype=\"multipart/form-data\" >\<input type=\"file\" id=\"upload\" name=\"upload\" /> <br />\<input type=\"submit\" value=\"Upload\" />\</form>";m_respond.AppendBody(form);m_respond.AppendBodyHtmlEnd();m_respond.Flush(m_s);return true;}

        功能相当的简单。前面是输出了一下请求信息,方便调试。中间提取文件名和内容,然后直接把内容写到文件中,相当的粗暴。最后是输出了form,提交到自身。

三、更多讨论

        我这里是只是验证功能,正常情形这种功能肯定是和特定需求相关的,除了保存文件肯定还有很多相关处理,以这个为基础,做成特定页面当然是没有问题的。

        我这里是接收完整个请求才处理的,因为以前没考虑这个功能。如果文件很大,恐怕这个方式不行,需要专门优化。

(这里是结束)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/791597.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java实现两数相除

题意 给你两个整数&#xff0c;被除数 dividend 和除数 divisor。将两数相除&#xff0c;要求不使用乘法、除法和取余运算。 整数除法应该向零截断&#xff0c;也就是截去&#xff08;truncate&#xff09;其小数部分。例如&#xff0c;8.345 将被截断为 8 &#xff0c;-2.7335…

用DasViewer浏览模型时可以移动模型的中心点吗?

按住鼠标中键&#xff0c;就正常平移模型了。 DasViewer是由大势智慧自主研发的免费的实景三维模型浏览器,采用多细节层次模型逐步自适应加载技术,让用户在极低的电脑配置下,也能流畅的加载较大规模实景三维模型,提供方便快捷的数据浏览操作。 DasViewer下载地址&#xff1a;…

leetcode 热题 100(部分)C/C++

leetcode 热题 100 双指针 盛最多水的容器 【mid】【双指针】 思路&#xff1a; 好久没写代码sb了&#xff0c;加上之前写的双指针并不多&#xff0c;以及有点思维定势了。我对双指针比较刻板的印象一直是两层for循环i&#xff0c;j&#xff0c;初始时i,j都位于左界附近&…

SAM Self-Attention based Deep Learning Method

一、Why(Research Background) 网络流量分类根据协议(如超文本传输协议或域名系统)或应用程序(如脸书或Gmail)对流量类别进行分类。其准确性是一些网络管理任务(如服务质量控制、异常检测等)的关键基础。为了进一步提高流量分类的准确性,最近的研究引入了基于深度学习的方法…

Open CASCADE学习|刚体( TopoDS_Shape)按某种轨迹运动,停在指定位置上

今天实现如下功能&#xff1a;刚体做做螺旋运动&#xff0c;轨迹已知&#xff0c;求刚体在每个位置上的所占据的空间&#xff0c;就是把刚体从初始位置变换到该位置。 这里的刚体是一个砂轮截面&#xff0c;螺旋运动轨迹由B样条曲线拟合&#xff0c;通过Frenet标架确定运动轨迹…

datalist是什么,有什么作用?

<datalist>标签用于定义一个预定义选项列表&#xff0c;它可以与文本输入框&#xff08;<input type"text">&#xff09;一起使用&#xff0c;提供一组可选的值供用户选择或输入。<datalist>标签中的选项可以通过<option>标签来定义。 <…

多线程(29)Semaphore

Semaphore&#xff08;信号量&#xff09;是一种常用的并发控制技术&#xff0c;用于管理对一组资源的访问控制。信号量的核心是一个计数器&#xff0c;表示可用资源的数量。计数器的值可以初始化为任意值&#xff0c;如果初始化为1&#xff0c;则成为一个互斥锁&#xff08;Mu…

iOS使用CoreML运用小型深度神经网络架构对图像进行解析

查找一个图片选择器 我用的是ImagePicker 项目有点老了&#xff0c;需要做一些改造&#xff0c;下面是新的仓库 platform :ios, 16.0use_frameworks!target learnings dosource https://github.com/CocoaPods/Specs.gitpod ImagePicker, :git > https://github.com/KevinS…

Python之Opencv进阶教程(1):图片模糊

1、Opencv提供了多种模糊图片的方法 加载原始未经模糊处理的图片 import cv2 as cvimg cv.imread(../Resources/Photos/girl.jpg) cv.imshow(girl, img)1.1 平均值 关键代码 # Averaging 平均值 average cv.blur(img, (3, 3)) cv.imshow(Average Blur, average)实现效果 1.2…

STM32F407 FSMC并口读取AD7606

先贴一下最终效果图.这个是AD7606并口读取数据一个周期后的数据结果. 原始波形用示波器看是很平滑的. AD7606不知为何就会出现干扰, 我猜测可能是数字信号干扰导致的. 因为干扰的波形很有规律. 这种现象基本上可以排除是程序问题. 应该是干扰或者数字信号干扰,或者是数字和模拟…

基于Spring Boot的餐厅点餐系统

基于Spring Boot的餐厅点餐系统 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;Maven3.3.9 部分系统展示 管理员登录界面 用户注册登录界面 …

​如何使用ArcGIS Pro进行洪水淹没分析

洪水淹没分析是一种常见的水文地理信息系统应用&#xff0c;用于模拟和预测洪水事件中可能受到淹没影响的地区&#xff0c;这里为大家介绍一下ArcGIS Pro进行洪水淹没分析的方法&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的DEM数据&…

Python学习笔记-Flask接收post请求数据并存储数据库

1.引包 from flask import Flask, request, jsonify from flask_sqlalchemy import SQLAlchemy 2.配置连接,替换为自己的MySQL 数据库的实际用户名、密码和数据库名 app Flask(__name__) #创建应用实列 app.config[SQLALCHEMY_DATABASE_URI] mysqlpymysql://ro…

优化 Nginx 处理 504 Gateway Timeout 错误

简介 504 Gateway Timeout 错误表示 Nginx 在指定的时间内没有从上游服务器收到响应。这可能是由于上游服务器处理时间过长或网络连接问题造成的。 调整 Nginx 参数 为了解决 504 错误&#xff0c;可以调整以下 Nginx 参数&#xff1a; **proxy_read_timeout&#xff1a;**指…

如何判断驱动中probe是否执行

在我们调试驱动程序的时候需要查看probe函数是否执行&#xff0c;我们只需要在其probe函数写一个printk函数即可&#xff0c;在驱动和设备匹配之后就会执行这个probe里面的打印函数 但是前提我们需要降低内核的打印级别&#xff0c;否则是看不到的&#xff0c;我们可以降到最低…

mac电脑maven配置环境变量

1、下载maven https://maven.apache.org 2、配置环境变量 vim .bash_profile JAVA_HOME/Library/Java/JavaVirtualMachines/jdk-1.8.jdk/Contents/Home PATH$JAVA_HOME/bin:$PATH export JAVA_HOME export PATH#maven export MAVEN_HOME/Users/haines/desktop/work/java/a…

鸿蒙OS开发实例:【应用事件打点】

简介 传统的日志系统里汇聚了整个设备上所有程序运行的过程流水日志&#xff0c;难以识别其中的关键信息。因此&#xff0c;应用开发者需要一种数据打点机制&#xff0c;用来评估如访问数、日活、用户操作习惯以及影响用户使用的关键因素等关键信息。 HiAppEvent是在系统层面…

适用于 Linux 的 Windows 子系统安装初体验

1、简述 Windows Subsystem for Linux (WSL) 是 Windows 的一项功能&#xff0c;允许您在 Windows 计算机上运行 Linux 环境&#xff0c;而无需单独的虚拟机或双重启动。 WSL 旨在为想要同时使用 Windows 和 Linux 的开发人员提供无缝且高效的体验。 使用 WSL 安装和运行各种 L…

【javaScript】DOM编程入门

一、什么是DOM编程 概念&#xff1a;DOM(Document Object Model)编程就是使用document对象的API完成对网页HTML文档进行动态修改&#xff0c;以实现网页数据和样式动态变化的编程 为什么要由DOM编程来动态修改呢&#xff1f;我们就得先理解网页的运行原理&#xff1a; 如上图&a…

Map之computeIfAbsent

Map之computeIfAbsent Absent /ˈbsənt , bˈsent/ ab相反s存在ent…的 从map中获取key对应的value,如果value不存在就用提供的Function创建一个新的value,然后存入map,最后返回 优化前 Map<String, Set<Pet>> statistics new HashMap<>(); Set<Pet…