# py3SimpleHTTPServerWithUpload
**Repository Path**: iubest/py3SimpleHTTPServerWithUpload
## Basic Information
- **Project Name**: py3SimpleHTTPServerWithUpload
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2021-01-23
- **Last Updated**: 2021-01-23
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## Usage:
```bash
$ python3 py3SimpleHTTPServerWithUpload.py [port]
````
然后在浏览器输入 `http://[ip]:[port]`, 其中 `port` 默认是 `8000`, 就能上传或者下载文件了。注意把 `[ip]` 换成 `server` 的局域网 `ip` 地址。
注: 本模块仅作为局域网内小文件共享的工具, 不建议长时间在后台运行.
## Workflow
* 确定边界
假设有文件 `file1.txt`, 其内容如下
```
content in file1
hello file1
```
和 `file2.txt`, 其内容如下
```
content in file2
hello file2
```
如果上传这两个文件, 则在 `HTTP headers` 之后是这样的 `POST data`
```
------WebKitFormBoundaryLVlRNkjiiJLtNYQE
Content-Disposition: form-data; name="file"; filename="file1.txt"
Content-Type: text/plain
content in file1
hello file1
------WebKitFormBoundaryLVlRNkjiiJLtNYQE
Content-Disposition: form-data; name="file"; filename="file2.txt"
Content-Type: text/plain
content in file2
hello file2
------WebKitFormBoundaryLVlRNkjiiJLtNYQE--
```
也就是根据这样的 `boundary_start = '------WebKitFormBoundary.....'` 分隔符作为 `POST data` 的边界, 而 `boundary` 可以在 `HTTP headers` 的 `Content-Type` 后面找到
```python
def deal_post_data(self):
print('Content-Type: %s' % self.headers['Content-Type'])
# Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryAbX7GIRMBXOOJj8p
boundary = self.headers["Content-Type"].split("=")[1]
# ----WebKitFormBoundaryAbX7GIRMBXOOJj8p
```
读取 `POST data` 的测试代码如下
```python3
def deal_post_data(self):
boundary = self.headers["Content-Type"].split("=")[1]
remainbytes = int(self.headers['Content-length'])
line = self.rfile.readline().decode('utf-8')
print(line)
remainbytes = remainbytes - len(line)
if not boundary in line:
return (False, "Content NOT begin with boundary")
while remainbytes > 0:
line = self.rfile.readline().decode('utf-8')
print(line, end='')
remainbytes = remainbytes - len(line)
return(True, '')
```
* 区块格式
每一块区域都是这样的格式
```
------WebKitFormBoundary...
filename
Content-Type
Content
```
* 终止条件
如果是最后一个文件, 那么在其 `Content` 之后会加上一行
```
------WebKitFormBoundary...--
```
出来了一个小尾巴, 而且后面没有数据了, `remainbytes=0`, 这两个条件都可以作为我们的终止条件, 也就是说
```
boundary = '----WebKitFormBoundary...'
boundary_start = '--' + boundary
boundary_end = '--' + boundary + '--'
```
* 读取数据
有了以上条件我们就可以从数据流中读取并保存内容了(具体代码请见 `deal_post_data()`)
## Notes:
* 文件夹 `./py2_SimpleHTTPServerWithUpload/` 中存放的是基于 `python2.7` 的模块,在此感谢 [bones7456](http://luy.li/2010/05/15/simplehttpserverwithupload/) 同学和 [BUPTGuo](http://buptguo.com/2015/11/07/simplehttpserver-with-upload-file/) 同学的成果。
注意, 最好用完就退出程序, 而不要一直启动, 以防
* 由于 `python2.7` 和 `Python3.4` 有较多不同特性,原来的程序不能直接在 `python3.4` 的环境中运行,因此我根据以上两位同学的思路,进行了部分重写,制作了基于 `python3.4` 的模块。主要改动如下:
* 移除了 `StringIO`,不使用 `copyfile()`
需要传输的信息全都用 `str`, 处理完逻辑后再用 `utf-8` 编码为 `bytes object`,然后放到 `wfile` 上进行传输
* 修改 `html` 的部分标签顺序
* 根据 @`a.7` 同学的建议, 在上一版本的基础上进行了改进, 使我们可以一次性上传多个文件
* 更多内容请看[这篇博客](https://jjayyyyyyy.github.io/2016/10/07/reWrite_SimpleHTTPServerWithUpload_with_python3.html)
* 原项目地址: https://github.com/jJayyyyyyy/cs/tree/master/just%20for%20fun/file_transfer/http
现在独立成为一个 `repo`, 方便查找和使用, 也便于维护和升级
* TODO
* 步骤:如何安装自己写的模块
* 更新博客
## Update
* 20181029
* 根据 @`a.7` 同学的建议, 在上一版本的基础上进行了改进, 使我们可以一次性上传多个文件
多个文件直接也是用 `boundary` 进行分隔的, 我们正是利用了这点对多个文件进行 `POST`, 我们也可以利用这点区分不同的文件
* 更新 `deal_post_data()`, 根据 `boundary` 重写逻辑, 以此取代 `remainbytes`
* 更新 `list_directory()`,
根据 [w3schools](https://www.w3schools.com/tags/att_input_multiple.asp), 在 `` 标签中增加 `multiple="multiple"` 属性, 可以 `accepts multiple values`
* 增加注释
* 更新 `readme.md`
* 修复博文链接
* 把空格换成了 `tab`, `4 space = 1 tab`
* 20181030
* 根据 @`Liu William` 同学的建议, 增加了 `port` 参数
* 20190113
* 根据 @`YLXDXX` 同学的反馈, 原来的程序在连续上传多个相同名字的文件时, 会覆盖之前的文件, 现已修复, 新的命名规则如下(假设待上传文件的名字是 `readme.md` )
* 如果文件名不存在, 则保存为 `readme.md`
* 如果已经存在 `readme.md` 文件, 则新文件命名为 `readme_1.md`. 如果已经存在 `readme_1.md`, 则命名为 `readme_2.md`, 依次类推.
* 20190128
* @`a.7` 同学发现了一个 `bug`, 新上传的文件会在文件末尾多出两个字节的内容, 分析后发现, `Post` 数据的实际内容, 在 `boundary_end` 之前那一行就已经结束了, 而这一行数据后面紧跟的 `'\r\n'` 只是为了区分接下来的 `boundary_end`. 因此在把数据写如文件的时候, 要把这个多余的 `'\r\n'` 去掉
* 修改写入文件的方法. 原来的做法是读一行写一行. 现在的做法是, 先写入缓冲区 `buf`, 当 `buf` 大于 `1024 Bytes` 时, 一次性将 `buf` 写入文件, 以此提高写入效率.
* 感谢 @`xmq` 同学的测试
* 更新注释, 更新 `readme`