背景:
有个需求是要求下载文件并将其改名,故当时将功能做至项目上,导致文件下载流程如下:
附件服务器 - 下载 - > 项目服务器- 改名 - > 用户
相当于下载了两个文件,再加上带宽附件服务器带宽2M,用户体验差,故考虑去掉中间步骤,用户直接向附件服务器请求文件。
服务器环境:
- Windows Server 2008
- IIS 7
- PHP 5.4
过程:
pdf.js - > download2.js - > php
第一阶段:
pdf.js二次开发,做完回头一想,当时也是钻了牛角尖,但是也大体了解解其工作原理,了解Blob的一些知识;
思路:
先找JS文件中是哪里获取了文件名,进而切入改变其文件名,
在viewer.js中发现有变量名filename,故分析其为文件名,在下载方法中下断点,将其定值,下载名称改变,此处使用GetQueryString获取url中的参数值
function GetQueryString(name) {
let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)","i");
let r = window.location.search.substr(1).match(reg);
if (r!=null) return (r[2]); return null;
}
开始考虑自动下载,经过页面分析,发现右上方有下载键,故事考虑在window.onload中触发其点击。
触发点击下载:
window.onload = function () {
document.getElementById("download").click();
}
测试在Chorme中通过,转至IE测试,发现名称不改变,于是开始追溯代码
在viewer.js 796行附近,追踪至下载方法,阅读代码发现程序先创建一个节点
var a = document.createElement('a');
然后根据浏览器版本来判断a.click方法,暂且不提
发现它默认使用的是download属性,然而IE全家都不支持,跳转至下面直接点击当前a对象,
在这个过程中发现下载触发的方法会根据文档是否加载完成变化,如果文档加载完成,点击下载是走的navigator.msSaveBlob方法,该方法是微软自家的MS,用的Blob对象,但是存在的问题,就是也是相当于先加载一遍他的二进制流,然后再下载,速度也是慢的,对于大文件就得等它加载完后才能触发下载,不合理。
然后一直寻找解决方法,不得其果。
第二阶段:
pdf.js就是一个预览插件,我为啥偏要在上面下断点,二次开发,遂将目标转移至js下载插件download2.js
代码如下:
<html>
<head>
<script src="build/download2.js"></script>
</head>
<body>
<script>
let filename_get = decodeURI(GetQueryString('file_name'));
let file_path = GetQueryString('file_path');
let x=new XMLHttpRequest();
x.open("GET", file_path, true);
x.responseType = 'blob';
x.onload=function(e){
download(x.response, filename_get, 'application/pdf');
};
x.send();
function GetQueryString(name) {
let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)","i");
let r = window.location.search.substr(1).match(reg);
if (r!=null) return (r[2]); return null;
}
</script>
</body>
</html>
随便点了几个发现好用了,chorme IE都可以,以为成了,洋洋得意之时,点了一个比较大的文件,没有反应,检查网络,发现报错:
net :: ERR_INSUFFICIENT_RESOURCES 200
经查,发现有可能是服务器请求大小限制,故上服务器修改配置,因为比较常用nginx或apache,对IIS并不是很了解,略微了解了一下,将system.web中的MaxRequestLength修改为400M,重启iisrestart ,测试失败,一样不下载,并且该方式有个问题,他也是需要先搞到js中,然后再发送xhr请求,文件比较大的时候会有一段的无反应时间,体验较差
综合以上几个原因故放弃
第三阶段:
目光转回PHP上,个人理解,js作为一种客户端语言,服务端的配置会十分限制其功能,故意视线转回老本行,在服务器上安装PHP,部署等不再赘述。
里面有个点,因为这个只有一个域名解析至这个服务器,懒得再申请了,于是配置的时候端口选择了82,不知道这样会不会有暴露端口的危险,但是对于服务器运维不是很了解,故先这样实现,以后有问题再修改附下载代码:
<?php
ob_clean();
$filename = $_GET['filename'];
$filename = urldecode($filename);//传的参数encode了
$filepath = '/';
if(!file_exists($filepath)){
var_dump($filepath);
exit;
}
$fp=fopen($filepath,"r");
$filesize=filesize($filepath);
header("Content-type:application/octet-stream");
header("Accept-Ranges:bytes");
header("Accept-Length:".$filesize);
header("Content-Disposition: attachment; filename=".$filename);
$buffer=1024;
$buffer_count=0;
while(!feof($fp)&&$filesize-$buffer_count>0){
$data=fread($fp,$buffer);
$buffer_count+=$buffer;
echo $data;
}
fclose($fp);
?>
总结:
其实一开始不想用PHP解决的,单纯的用用js搞定,但是发现js基础还是薄弱哈哈,记录本次思路,以供后期翻阅。