假设我有网站上文件的下载链接。
单击这些链接时,会向服务器发送一个AJAX请求,该请求返回带有文件位置的URL。
我想要做的是在响应回来时指示浏览器下载文件。有可行的方法吗?
答案 0 :(得分:24)
我们这样做: 首先添加此脚本。
<script type="text/javascript">
function populateIframe(id,path)
{
var ifrm = document.getElementById(id);
ifrm.src = "download.php?path="+path;
}
</script>
将它放在您想要下载按钮的位置(这里我们只使用链接):
<iframe id="frame1" style="display:none"></iframe>
<a href="javascript:populateIframe('frame1','<?php echo $path; ?>')">download</a>
文件'download.php'(需要放在你的服务器上)只包含:
<?php
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=".$_GET['path']);
readfile($_GET['path']);
?>
因此,当您单击该链接时,隐藏的iframe将获取/打开源文件'download.php'。使用path作为get参数。 我们认为这是最好的解决方案!
应该注意的是,这个解决方案的PHP部分是一个简单的演示,可能非常非常不安全。它允许用户下载任何文件,而不仅仅是预定义的集合。这意味着他们可以下载网站本身的部分源代码,可能包含API凭证等。
答案 1 :(得分:18)
我创建了一个开源jQuery File Download plugin(Demo with examples)(GitHub),这也可以帮助您解决问题。它与iframe非常相似,但有一些很酷的功能,我发现它非常方便:
答案 2 :(得分:14)
阅读答案 - 包括接受的答案我想指出通过GET直接将路径传递给readfile的安全隐患。
对某些人来说似乎很明显,但有些人可能只是复制/粘贴此代码:
<?php
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=".$_GET['path']);
readfile($_GET['path']);
?>
那么如果我将'/ path / to / fileWithSecrets'之类的内容传递给此脚本会发生什么? 给定的脚本将很乐意发送webserver用户可以访问的任何文件。
有关如何防止此问题的信息,请参阅此讨论:How do I make sure a file path is within a given subdirectory?
答案 3 :(得分:10)
如果这是您自己的服务器应用程序,那么我建议使用以下标题
Content-disposition: attachment; filename=fname.ext
这将强制任何浏览器下载文件,而不是在浏览器窗口中呈现它。
答案 4 :(得分:7)
只需从您的javascript调用window.location.href = new_url
,它就会将浏览器重定向到该网址,因为用户已将其输入地址栏
答案 5 :(得分:3)
同意maxnk提到的方法,但是您可能需要重新考虑尝试自动强制浏览器下载URL。它可能适用于二进制文件,但对于其他类型的文件(文本,PDF,图像,视频),浏览器可能希望在窗口(或IFRAME)中呈现它而不是保存到磁盘。
如果你真的需要进行Ajax调用以获得最终的下载链接,那么使用DHTML动态地将下载链接(从ajax响应)写入页面呢?这样,用户可以点击它下载(如果是二进制)或在浏览器中查看 - 或者在链接上选择“另存为”以保存到磁盘。这是一个额外的点击,但用户有更多的控制权。
答案 6 :(得分:3)
试试这个lib https://github.com/PixelsCommander/Download-File-JS它比以前描述的所有解决方案都更现代,因为它使用“下载”属性和方法组合来带来最佳体验。
在此解释 - http://pixelscommander.com/en/javascript/javascript-file-downliading-ignore-content-type/
似乎是用JavaScript开始下载的理想代码。
答案 7 :(得分:3)
要解决最高投票答案中的安全漏洞,您可以将iframe src直接设置为您想要的文件(而不是中间的php文件),并在.htaccess文件中设置标头信息:
<Files *.apk>
ForceType application/force-download
Header set Content-Disposition attachment
Header set Content-Type application/vnd.android.package-archive
Header set Content-Transfer-Encoding binary
</Files>
答案 8 :(得分:2)
我建议在页面上创建一个不可见的iframe,并将它的src设置为你从服务器收到的url - 下载将在没有页面重新加载的情况下开始。
或者您可以将当前document.location.href设置为收到的网址。但如果请求的文档实际上不存在,那么这可能会导致用户看到错误。
答案 9 :(得分:2)
关于最佳答案,我有可能解决安全风险。
<?php
if(isset($_GET['path'])){
if(in_array($_GET['path'], glob("*/*.*"))){
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=".$_GET['path']);
readfile($_GET['path']);
}
}
?>
使用glob()函数(我在下载文件的路径中测试了下载文件)我能够制作一个快速的文件数组,这些文件是“允许的”#34;下载并检查传递的路径。这不仅可以确保被抓取的文件不是敏感的,而且还可以同时检查文件是否存在。
〜注意:Javascript / HTML~
HTML:
<iframe id="download" style="display:none"></iframe>
和
<input type="submit" value="Download" onclick="ChangeSource('document_path');return false;">
JavaScript的:
<script type="text/javascript">
<!--
function ChangeSource(path){
document.getElementByID('download').src = 'path_to_php?path=' + document_path;
}
-->
</script>
答案 10 :(得分:1)
我建议window.open()
打开一个弹出窗口。如果是下载,则没有窗口,您将获得您的文件。如果有404或其他东西,用户将在新窗口中看到它(因此,他们的工作不会受到打扰,但他们仍然会收到错误消息)。
答案 11 :(得分:1)
当你需要的是将浏览器重定向到不同的window.location.href时,你为什么要制作服务器端?
以下代码解析?file = QueryString(taken from this question)并在1秒内将用户重定向到该地址(即使在Android浏览器上也适用于我):
<script type="text/javascript">
var urlParams;
(window.onpopstate = function () {
var match,
pl = /\+/g, // Regex for replacing addition symbol with a space
search = /([^&=]+)=?([^&]*)/g,
decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
query = window.location.search.substring(1);
urlParams = {};
while (match = search.exec(query))
urlParams[decode(match[1])] = decode(match[2]);
})();
(window.onload = function() {
var path = urlParams["file"];
setTimeout(function() { document.location.href = path; }, 1000);
});
</script>
如果您的项目中有jQuery,请务必删除那些window.onpopstate&amp; window.onload处理程序并在$(document).ready(function(){});
中执行所有操作