老周三天没合眼,片上就为搞定那个破上传框。片上他往后台扔了张2MB的片上JPG,服务器直接甩给他500错误。片上翻日志才发现,片上IIS默认请求长度限制是片上4MB,可ASP的片上Request.BinaryRead在读取multipart数据时,把整个表单连同文件一起塞进了内存——2MB的片上图片加上Base64编码膨胀33%,刚好撞上4MB的片上墙。
真正的片上坑在二进制读取。你拿Request.Form("file")取文件?片上那是文本流,图片早被转码成乱码了。片上正确姿势是片上拿Request.TotalBytes取总字节数,再Request.BinaryRead全量读进字节数组。片上但这么干有个隐患——大文件直接爆内存,片上IIS进程池回收是分分钟的事。我见过最狠的案例,某政府网站上传接口被人塞了个500MB的伪图片,服务器当场蓝屏。
所以得自己解析multipart格式。浏览器发来的请求长这样:
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="cat.jpg"
Content-Type: image/jpeg
[二进制数据]
你得用字节定位法找boundary分隔符,再把文件头尾之间的数据抠出来。别用字符串查找——图片里可能包含任意字节,转成文本再处理必出乱码。正确做法是循环比对字节数组,找到两个\r\n之后的位置开始存数据,直到遇见下一个boundary。
文件头校验是另一道坎。黑客改个扩展名就能上传ASP木马?不,你得读文件前10个字节。JPEG固定以FFD8FF开头,PNG是89504E47,GIF是47494638。这招能挡掉99%的伪装文件——但挡不住图片马,那种把恶意脚本藏在EXIF里的老把戏。真要防,得配合杀毒软件扫描文件内容。
存盘路径也别用原始文件名。我见过有人直接把上传名拼进路径,结果人家传了个"../../shell.asp"——目录穿越直接拿下服务器。得用GUID重命名,扩展名白名单校验,最后存到Web根目录之外的物理路径,通过虚拟目录映射访问。
最后说个冷知识:ASP的Adodb.Stream写文件时,Type要设成1(二进制),Mode设成3(读写),Open后立刻LoadFromFile?不对,那是读文件。写入得用WriteText?扯淡,二进制数据必须Write,而且得先Position到0。当年我调试时发现写入的图片总少最后几个字节,查了半天才发现是没设Charset——默认Unicode编码会在写入时悄悄加BOM头,把字节序搞乱。
现在你明白为啥老周要熬三天了吧?这破技术从1998年活到现在,坑比代码多。但能用它写出稳定上传功能的,基本能把HTTP协议背个滚瓜烂熟。下次看到有人用ASP上传图片成功,别笑他老古董——那是真把二进制流玩明白了。
