这个其实是一个一致很困扰的问题,虽然以前大体可以定位是因为python的requests库原因,导致file的filename属性在上传的时候不能正确的被编码,故服务器接收失败。但是之前这个并不是很影响发种姬发布(实际被影响的种子只有几个)。故一直没有去解决。
但最近在配置新的发种姬的时候,却发现所有种子、所有站点都出现了同样的错误。那么就说明了这个问题需要解决了23333

额,如果你看不下去中间过程,请直接翻阅到最底下就行233333
环境准备
构造demo站点
为了更明确的定位问题所在,建个demo站点。NexusPHP构架建立站点特别简单,唯一需要注意的是使用的PHP版本不能太高,因为np构架使用了高版本PHP版本抛弃的mysql方法库,所以我选择了php 5.5.38。
另,不建议使用Github上的某官方源码 ZJUT/NexusPHP ,因为实测,这个版本引入了 UCenter 但很明显没有完成(缺少相关的数据库构造语句)。更推荐使用SourceForge上的 源码 进行搭建。

搭建过程不是本文重点,你随便搜索个教程应该就能解决。(实际是这个demo站点很早之前就建好了,忘了当初是怎么建的了
服务器端takeupload.php
根据NP源码的takeupload.php分析,POST的body部分缺少descr、type、name、file字段时,会提示缺少必填项目,且字段缺少的情况在最开始的地方进行判断。且如果使用网页提交空表单的话,一般会进入后面的std_empty_filename(即提示信息为文件名不能为空! )
相关参考代码(站点均可能对该部分源码进行修改)如下:
1 | foreach(explode(":","descr:type:name") as $v) { |
但这部分代码并不明确,故修改使之明确缺少信息。修改后的代码如下
1 | foreach(explode(":","descr:type:name") as $v) { |
使用Fiddler4抓包
在服务器与客户端之间使用Fiddler代理抓包,代理信息为127.0.0.1:8888。
在requests中这样设置代理
1 | import requests |
本地客户端临时Python脚本
首先更新本地的requests库到最新的2.18.4。(因为原先发种姬使用的2.9.1并没有出现很严重的问题)
并按照我们原先的发布方法新写一个基本的发布脚本。其源码如下:
1 | # !/usr/bin/python3 |
试错
第一次尝试 (失败)
运行前面刚写的临时Python脚本,我得到了一个很奇怪的报错。
1 | D:\Anaconda3\python.exe E:/Github_repo/Byrbt-Autoseed/test.py |
很奇怪,丢失的信息是descr。通过Fiddler 4抓包信息可以看到,我们非文件的信息提交也被当做了文件提交,故PHP在使用$_POST从POST信息中获取参数时就获取不到正确的信息。

修改服务器相关代码为下列后,重新运行脚本。
1 | foreach(explode(":","descr:type:name") as $v) { |
得到以下返回
1 | D:\Anaconda3\python.exe E:/Github_repo/Byrbt-Autoseed/test.py |
可见在requests(2.18.4)库中,我们原先使用的方法是存在问题的——所有非文件的会被错误的编码成文件。而导致服务器端程序不能正确获取值。
第二次尝试 (失败)
那么如果不能使用files的话,那使用data会怎么样?
修改脚本为下列后,重新运行脚本。
1 | r = requests.post(post_url,cookies=cookies_raw2jar(raw_cookies),data=tup,proxies=proxies) |
得到以下返回
1 | D:\Anaconda3\python.exe E:/Github_repo/Byrbt-Autoseed/test.py |
看来困扰之前的问题解决了。descr等字段已经被服务器正确的解析了。但file并没有,同时Fiddler抓包显示的信息表示上传的已经不是 multipart/form-data 的形式。且file被错误的提交成了三个字段。
所以直接使用data属性也是不正确的举措。

第三次尝试
如果这样的话,那么复用files和data会这么样那?我们继续修改Python脚本。
1 | file_tup = [ |
这次很好,直接返回了我们想要的结果,同时Fiddler抓包也显示了正确的requests body。
1 | D:\Anaconda3\python.exe E:/Github_repo/Byrbt-Autoseed/test.py |

第四次尝试 (失败)
我们重新回到requests提供的files属性上,通过查阅源代码备注,我们可以看到以下提示
1 | files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. |
那么是不是我们之前写的files元组出错了那?
1 | tup1 = ( |
更改代码并分别运行,结果均显示上传失败!std_missing_form_data from POST, which is descr.In FILES it's Array。且Fiddler抓包结果与第一次尝试相同。
正确的发布脚本应该怎样
通过上面的尝试,我发现了在最新的requests库中,在files属性中的信息均会被编码成file而被发送,不与之前的编写时候的结果一致。其他非文件上传信息应该与文件信息分开放在data属性中。经过上面一步步试错,我们知道了正确的应该复用data与files(即文件放在files中发,字段放在data中发)才能正确的向服务器发送正确的信息。
1 | # !/usr/bin/python3 |
但这样还有问题没有解决。我们更改下种子文件。这样种子文件名中是带有特殊的,不能被ascii编码的字符。
1 | torrent_file = r"F:\[BYRBT].[BeanSub&FZSD][Saiki_Kusuo_no_Ψ-nan_S2][20][GB][720P][x264_AAC].mp4.torrent" |
运行脚本,提示上传失败!std_missing_form_data, which is file,但Fiddler抓包结果显示正常,唯一不同的是filename那里显示的和正常的有些不同。
虽然暂时不是很明白是requests库的问题还是PHP处理的问题,(Ps. 我觉得是requests的问题可能更大些。。

因为np的机制,上传后的种子名字在存储的时候以 <tid>.torrent 存储,而显示以及下载的时候是以做种时选择的文件夹以及文件名为主,所以移除影响不大。
所以你可以这么写233333
1 | file_tup = ("file", ("rhilip.torrent", open(torrent_file, 'rb'), 'application/x-bittorrent')), |
当然最好的还是用编码的形式先移除这些non-ascii的字符来使种子名正确的被requests库处理。
原理大体这样: Python3: str -> bytes -> str
1 | >>>"[BYRBT].[BeanSub&FZSD][Saiki_Kusuo_no_Ψ-nan_S2][20][GB][720P][x264_AAC].mp4.torrent".encode("ascii", errors="ignore").decode() |
重新修改,那么最终我们得到一个在requests(2.18.4)下,可以用的发布脚本,撒花*★,°:.☆( ̄▽ ̄)/$:.°★* 。
1 | # !/usr/bin/python3 |
