用JavaScript开始文件下载

假设我有我的网站上的文件的下载链接。

点击时,这些链接发送一个AJAX请求到服务器,该服务器返回带有文件位置的URL。

我想要做的是直接浏览器下载响应回来的文件。 有没有一个便携式的方式来做到这一点?

我们这样做:首先添加这个脚本。

<script type="text/javascript"> function populateIframe(id,path) { var ifrm = document.getElementById(id); ifrm.src = "download.php?path="+path; } </script> 

把它放在你想要下载button的地方(这里我们只用一个链接):

 <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参数。 我们认为这是最好的解决scheme!

应该指出,这个解决scheme的PHP部分是一个简单的演示,可能非常非常不安全。 它允许用户下载任何文件,而不仅仅是一个预定义的集合。 这意味着他们可以下载网站本身的部分源代码,可能包含API凭证等。

我创build了一个开源的jQuery文件下载插件 ( 示例演示 )( GitHub ),这也可以帮助您的情况。 它与iframe的工作方式非常类似,但有一些很酷的function,我发现它非常方便:

  • 用户永远不会离开他们发起文件下载的同一页面。 这个function正在成为现代Web应用的关键
  • 经过testing的跨浏览器支持(包括手机!)
  • 它以类似于jQuery的AJAX API的方式支持POST和GET请求
  • successCallback和failCallback函数允许您明确地了解用户在两种情况下看到的内容
  • 与jQuery UI相结合,开发人员可以轻松地显示一种模式,告诉用户正在进行文件下载,在下载开始后解除模式,甚至以友好的方式通知用户发生了错误。 有关此示例,请参阅演示。

阅读答案 – 包括接受的答案,我想指出通过GET直接传递path到readfile的安全含义。

这可能看起来很明显,但有些可能只是复制/粘贴此代码:

 <?php header("Content-Type: application/octet-stream"); header("Content-Disposition: attachment; filename=".$_GET['path']); readfile($_GET['path']); ?> 

那么如果我把诸如'/ path / to / fileWithSecrets'这样的东西传给这个脚本会发生什么? 给定的脚本将愉快地发送web服务器用户有权访问的任何文件。

请参阅此讨论以了解如何防止此问题: 如何确保文件path在给定的子目录中?

如果这是你自己的服务器应用程序,那么我build议使用下面的标题

 Content-disposition: attachment; filename=fname.ext 

这将强制任何浏览器下载文件,而不是在浏览器窗口中显示它。

只需从javascript调用window.location.href = new_url ,并将浏览器redirect到该URL,因为用户在地址栏中input了该URL

一个赞同的方法提到的maxnk,但是你可能想重新考虑尝试自动强制浏览器下载的URL。 对于二进制文件,对于其他types的文件(文本,PDF,图像,video),它可能正常工作,浏览器可能想要将其渲染到窗口(或IFRAME)中,而不是保存到磁盘。

如果你真的需要做一个Ajax调用来获得最终的下载链接,那么使用DHTMLdynamic地将下载链接(从ajax响应)写入页面呢? 这样用户可以点击它来下载(如果是二进制)或在浏览器中查看 – 或者select链接上的“另存为”保存到磁盘。 这是一个额外的点击,但用户有更多的控制。

为了解决顶级答案中的安全漏洞,你可以将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> 

我build议在页面上创build一个隐形的iframe,并将其设置为从服务器接收到的URL – 下载将在没有页面重新加载的情况下启动。

或者你可以将当前的document.location.href设置为收到的url地址。 但是,如果请求的文件实际上不存在,这可能会导致用户看到一个错误。

关于最高的答案,我有一个可能的解决scheme的安全风险。

 <?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()函数(我testing下载文件的path是从要下载的文件中的一个文件夹)我能够快速地创build一个“允许”的文件的下载并检查传递的path。 这不仅确保被抓取的文件不是敏感的,而且同时检查文件的存在。

〜注: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> 

我build议window.open()打开一个popup窗口。 如果是下载,将不会有窗口,您将获得您的文件。 如果有404或者什么东西,用户会在新窗口看到它(因此,他们的工作不会被打扰,但他们仍然会得到一个错误信息)。

你为什么要做服务器端的东西,当你需要的是redirect浏览器到不同的window.location.href?

这里是parsing代码?file = QueryString( 取自这个问题 ),并在1秒内将用户redirect到该地址(即使在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&window.onload处理程序,并执行$(document).ready(function(){})中的所有操作。