Xiuno BBS上传附件的一个坑——导致上传大文件失败
Tillreetree 3月前

Xiuno BBS是一款优秀的轻论坛软件,它以其简洁的界面和易于使用的功能吸引了众多用户喵。

然而,没有任何东西是完美的喵。但是,好像也有一些小问题,比如附件上传,有些用户会遇到上传不成功的情况,这可真是让人困扰喵。

前人们提出了一些可能的原因和解决方案,

比如有人认为“是Nginx防火墙干扰了数据传输”,但这个观点并没有得到广泛认同,因为关闭防火墙可能会带来其他安全隐患喵。

还有人提出了一些其他解决方案,比如:

  • 设置白名单(对上传附件的地址开白名单)
  • 调整Nginx配置参数(主要与文件上传大小和超时时间有关)
  • 调整PHP上传文件参数(主要与文件上传大小和超时时间有关)
  • 更换到更高速的服务器
  • 图片用外链,文件用网盘(将所有的资源都弄到外面,自然就不需要附件了)

虽然这些方案在一定程度上可能有助于提高文件上传的成功率,但它们并没有从根本上解决Xiuno BBS上传附件的问题。事实上,这些可能的原因和解决方案全都是错误的喵。

接下来,我们可以深入分析这个问题的真正原因,并提供正确的解决方案喵。

首先,我们要了解一下Xiuno BBS上传附件的处理流程喵。

一、上传附件的处理流程

  1. 【前端】在发布帖子(或编辑帖子时),用户点击“上传附件”,然后选择一个文件。
  2. 【前端】首先将附件文件内容转换为base64编码
  3. 【前端】往后端的“上传附件”地址发送post请求,将文件名、文件信息和转换为base64编码后的文件数据将以application/x-www-form-urlencoded方式上传到服务器喵。
  4. 【后端】服务器收到文件名、文件信息和文件数据后,将文件数据进行base64解码
  5. 【后端】以“获取字符串长度”的方式获取文件大小,并比较是否超过最大上传文件大小(好像是20MB)。
  6. 【后端】将上传的文件数据存放到临时文件夹喵。
  7. 【后端】创建“临时附件”Session数组,并添加上传的这个文件喵。
  8. 【后端】最重要的是,没有及时销毁文件数据这个变量,可能会导致内存暴涨喵。

二、问题分析

这个上传附件的处理流程存在一个明显的坑:将附件文件内容转换为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),还有把刚才上传的文件也删掉喵。


这样子,问题就解决啦!文件上传功能已经可以正常工作了喵~

最后于 3月前 被Tillreetree编辑 ,原因: 简单快速的猫娘话:将句号替换成喵
最新回复 (9)
全部楼主
  • juse
    3月前 2
    0
    顶顶顶顶顶顶顶!!!
  • oliolo 版主
    3月前 3
    0
    相当不错,一般人搞不明白。
  • Tillreetree 版主 楼主
    3月前 4
    0
    oliolo 相当不错,一般人搞不明白。
    然后就会被随意转载,甚至都不愿意来个“原文链接”
  • glxhydue
    3月前 5
    0
    这个陈年老问题居然被解决了
  • Benladen
    3月前 6
    0
    面对如此强贴,论遇到多大阻力,只要我一息上尚存,就绝不会让它沉沦下去,这一点请楼主放心。 
  • h2so4
    3月前 7
    0
    沙发我没有,板凳我没有,板也没有,只好站在后面排队支持! 
  • 0
    版主可以给一份编辑好后的文件吗?
  • Tillreetree 版主 楼主
    2月前 9
    0
    繁华如三千东流水 版主可以给一份编辑好后的文件吗?
    我只提供思路
  • 07750775
    3天前 10
    0
    感谢楼主ing!!!
返回