使用JavaScript开始文件下载

62

假设我有一个网站的下载链接。

当这些链接被点击时,它们会发送一个AJAX请求到服务器,该请求返回包含文件位置URL的响应。

我的目标是在响应返回时让浏览器直接下载该文件。是否存在一种通用的方法来实现这一点?

12个回答

25

我们是这样做的: 首先添加此脚本。

<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',并将路径作为get参数传递。

需要注意的是,这个解决方案的PHP部分只是一个简单的演示,可能非常不安全。它允许用户下载任何文件,而不仅仅是预定义的文件集。这意味着他们可以下载网站本身的源代码的一部分,可能包含API凭据等重要信息。


6
@Fabio提供的代码 if (!file_exists($path)) { header("HTTP/1.1 404 Not Found"); exit; } 可以解决问题。该代码会检查文件是否存在,如果不存在,则会发送一个“404未找到”错误消息并退出程序。 - S B
52
有什么阻止用户查看源代码、获取download.php的URL,然后浏览到http://www.yoursite.com/download.php?path=config.php尝试获取你的密码呢?换句话说:请,请不要这样做。 - Grim...
非常感谢- 没有发送标头,我无法使其在IE或Chrome中工作。因此,此解决方案完美地解决了问题! - jazkat
5
跟进 @Grim... 的评论,他绝对是正确的,请不要以这种方式操作。直接将 iframe src 链接到文件,并使用 .htaccess 设置该特定文件类型的头信息。 - Matt K
1
一些安全的替代方案:1)将请求的路径与白名单进行比对,2)创建一个id=>文件路径映射表并使用该id。 - Svish
显示剩余5条评论

18
我已经创建了一个开源的jQuery文件下载插件带有示例的演示)(GitHub),它也可以帮助你解决问题。它的工作方式与iframe非常相似,但具有一些很棒的功能,我发现这些功能非常方便:
  • 用户从未离开他们启动文件下载的同一页。这个功能对于现代Web应用程序变得至关重要
  • 测试跨浏览器支持(包括移动设备!)
  • 它支持POST和GET请求,类似于jQuery的AJAX API
  • successCallback和failCallback函数允许您明确指定用户在任何情况下看到的内容
  • 结合jQuery UI,开发人员可以轻松地显示模态窗口,告诉用户正在进行文件下载,在下载开始后解除模态窗口,甚至以友好的方式通知用户发生了错误。请参见演示以获取示例。

你好。有没有办法防止Internet Explorer 8阻止下载?Internet Explorer信息栏会要求确认下载,这会导致页面重新加载并且无法下载,结果用户必须再次点击下载链接。 提前致谢。 - Pierpaolo
这正是我需要的,谢谢! - Tobia

14

只需在你的 JavaScript 中调用 window.location.href = new_url,浏览器就会重定向到该 URL,就像用户在地址栏中键入该 URL 一样。


13

阅读答案 - 包括被接受的答案,我想指出通过GET直接传递路径给readfile的安全隐患。

对于一些人来说这可能很显然,但有些人可能只是复制/粘贴此代码:

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

如果我将像“/path/to/fileWithSecrets”这样的内容传递给该脚本,会发生什么情况? 给定的脚本将愉快地发送任何Web服务器用户可以访问的文件。

有关如何防止此问题,请参考此讨论:如何确保文件路径在特定子目录内?


1
直接在iframe中链接到文件,并在htaccess中设置头文件。 - Matt K
这应该是一条注释,而不是一个答案。 - cemper93

10
如果这是你自己的服务器应用程序,我建议使用以下头部。
Content-disposition: attachment; filename=fname.ext

这将强制任何浏览器下载文件而不在浏览器窗口中呈现它。


5

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>

3
我同意maxnk提到的方法,但是您可能需要重新考虑尝试自动强制浏览器下载URL。对于其他类型的文件(文本、PDF、图像、视频),浏览器可能希望在窗口(或IFRAME)中呈现它,而不是保存到磁盘。
如果您确实需要进行Ajax调用以获取最终的下载链接,那么考虑使用DHTML将下载链接(从ajax响应)动态写入页面中怎么样?这样用户就可以单击它进行下载(如果是二进制文件),或在浏览器中查看 - 或者选择“另存为”链接以保存到磁盘。这是一个额外的点击,但用户更有控制权。

哦,是的,我忘记了非二进制文件,谢谢。 作为替代方案,可能将在服务器上对文件流进行包装并设置“Content-Disposition: attachment”标头是一个不错的解决方案。但这仅适用于小文件。 - maxnk
或者你可以尝试通过让服务器发送“Content-Type”头为“application/octet-stream”,来欺骗浏览器认为可下载的文件是二进制文件,而不管实际的文件类型。 - Marc Novakowski
1
Internet Explorer 8使用所有提到的方式(iframe,window.location.href或甚至“http-equiv = REFRESH CONTENT”)阻止文件下载。“为了帮助保护您的安全,IE阻止此网站向您的计算机下载文件...”。您知道有什么方法可以抑制此警告并开始下载,就像Firefox一样吗?我已经检查了几个网站:Skype,Google(带Picasa)和Download.com在下载时会导致警告(因为它们显示了某种“感谢”页面)。感谢您的想法! - Mar
在htaccess中设置该文件类型的头信息。 - Matt K

2

我建议在页面上添加一个不可见的iframe,并将其src设置为从服务器收到的url - 下载将在不重新加载页面的情况下开始。

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


什么时候从DOM中移除iframe才是安全的? - AMember

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()函数(我在下载文件的路径上方的一个文件夹中测试了下载文件),我能够快速创建一个允许下载的文件数组,并检查传递的路径是否与之匹配。这不仅确保所获取的文件不是敏感信息,同时也同时检查文件的存在性。
~注意: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>

没错,但这是在前一个答案的基础上建立的。我会加入一个类似的JavaScript连接。 - James Bordine II

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接