Xiuno BBS是一款优秀的轻论坛软件,它以其简洁的界面和易于使用的功能吸引了众多用户喵。
然而,没有任何东西是完美的喵。但是,好像也有一些小问题,比如附件上传,有些用户会遇到上传不成功的情况,这可真是让人困扰喵。
前人们提出了一些可能的原因和解决方案,
比如有人认为“是Nginx防火墙干扰了数据传输”,但这个观点并没有得到广泛认同,因为关闭防火墙可能会带来其他安全隐患喵。
还有人提出了一些其他解决方案,比如:
- 设置白名单(对上传附件的地址开白名单)
- 调整Nginx配置参数(主要与文件上传大小和超时时间有关)
- 调整PHP上传文件参数(主要与文件上传大小和超时时间有关)
- 更换到更高速的服务器
- 图片用外链,文件用网盘(将所有的资源都弄到外面,自然就不需要附件了)
虽然这些方案在一定程度上可能有助于提高文件上传的成功率,但它们并没有从根本上解决Xiuno BBS上传附件的问题。事实上,这些可能的原因和解决方案全都是错误的喵。
接下来,我们可以深入分析这个问题的真正原因,并提供正确的解决方案喵。
首先,我们要了解一下Xiuno BBS上传附件的处理流程喵。
一、上传附件的处理流程
- 【前端】在发布帖子(或编辑帖子时),用户点击“上传附件”,然后选择一个文件。
- 【前端】首先将附件文件内容转换为base64编码。
- 【前端】往后端的“上传附件”地址发送post请求,将文件名、文件信息和转换为base64编码后的文件数据将以
application/x-www-form-urlencoded
方式上传到服务器喵。
- 【后端】服务器收到文件名、文件信息和文件数据后,将文件数据进行base64解码。
- 【后端】以“获取字符串长度”的方式获取文件大小,并比较是否超过最大上传文件大小(好像是20MB)。
- 【后端】将上传的文件数据存放到临时文件夹喵。
- 【后端】创建“临时附件”Session数组,并添加上传的这个文件喵。
- 【后端】最重要的是,没有及时销毁文件数据这个变量,可能会导致内存暴涨喵。
二、问题分析
这个上传附件的处理流程存在一个明显的坑:将附件文件内容转换为base64编码,然后使用application/x-www-form-urlencoded
方式上传喵。这么做的缺点是,只要文件一大(也不需要很大,十几MB就够了),就可以让整个上传歇菜——也就是上传持续一分钟左右(约等于PHP的最大脚本运行时间)之后,请求就突然被取消了喵。
主要原因如下:
- base64编码后的数据比原始数据要大,其编码后的数据比原始数据大约增加33%左右。
- application/x-www-form-urlencoded方式不适用于大文件传输喵。这种编码方式在处理较小数据量时还行,但面对大文件传输时,可能导致内存占用过高,甚至导致请求失败喵。
那为啥还用urlencoded呢喵?
我个人猜测是为了兼容旧版本的IE浏览器(或者别的什么奇怪原因),Xiuno BBS才采用了application/x-www-form-urlencoded
方式上传文件喵。然而,这种兼容性问题在现代浏览器中已不再明显,因此继续使用这种方式可能并不明智喵。
三、解决方案
我们可以使用一个更适合大文件传输的上传方式,叫做multipart/form-data
喵。
这种方式可以把文件直接以二进制流的形式传输,这样就不用担心数据变大或者占用太多内存和带宽了喵。
3.1. 整体思路
3.1.1. 前端
当addattach有新的内容时,each_sync函数会处理选择的文件,这个步骤是没有问题的喵。但是,接下来的xn.upload_file就需要更换成jQuery的ajax函数了喵。参考以下参数:
url: xn.url('attach-create'),
type: 'POST',
contentType:false,
processData:false,
async: true,
data: this_file_data,
同时,我们需要使用new FormData()来处理文件数据喵。这个样子:
var this_file_data = new FormData();
this_file_data.append('name',file.name);
this_file_data.append('file',file);
温馨提示:使用jQuery的ajax函数时,success的回调函数的参数顺序是“服务器返回的字符串信息”和“状态”哦,别忘了把“服务器返回的字符串信息”用JSON.parse解析一下喵。
3.1.2. 后端
既然前端已经换了一种方式上传文件,那么后端也需要相应地做出改变喵。
首先,我们可以轻松地获取文件名变量:
$name = $_FILES['file']['name'];
然后,我们可以使用以下方式获取文件数据本身:
$data = file_get_contents($_FILES['file']['tmp_name'])
记得在file_put_contents之后unset($data),还有把刚才上传的文件也删掉喵。
这样子,问题就解决啦!文件上传功能已经可以正常工作了喵~
最后于 7月前
被Tillreetree编辑
,原因: 简单快速的猫娘话:将句号替换成喵