开始使用JavaScript下载文件

开始使用JavaScript下载文件,javascript,ajax,download,Javascript,Ajax,Download,假设我在我的网站上有文件的下载链接 单击这些链接时,会向服务器发送一个AJAX请求,服务器返回带有文件位置的URL 我想做的是,当响应返回时,引导浏览器下载文件。有没有一种可移植的方法可以做到这一点?我建议在页面上创建一个不可见的iframe,并将其src设置为您从服务器收到的url-下载将在不重新加载页面的情况下开始 或者,您可以将当前document.location.href设置为接收的url地址。但是,如果请求的文档实际上不存在,用户可能会看到错误。A同意maxnk提到的方法,但是您可能

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

单击这些链接时,会向服务器发送一个AJAX请求,服务器返回带有文件位置的URL


我想做的是,当响应返回时,引导浏览器下载文件。有没有一种可移植的方法可以做到这一点?

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


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

A同意maxnk提到的方法,但是您可能需要重新考虑尝试自动强制浏览器下载URL。它可能适用于二进制文件,但对于其他类型的文件(文本、PDF、图像、视频),浏览器可能希望在窗口(或IFRAME)中渲染,而不是保存到磁盘


如果您确实需要通过Ajax调用来获取最终的下载链接,那么使用DHTML将下载链接(来自Ajax响应)动态写入页面怎么样?通过这种方式,用户可以点击它下载(如果是二进制的),或者在浏览器中查看,或者在链接上选择“另存为”保存到磁盘。这是一个额外的点击,但是用户有更多的控制权。

我建议
window.open()
打开一个弹出窗口。如果是下载,将没有窗口,您将获得您的文件。如果有404或其他东西,用户将在新窗口中看到它(因此,他们的工作不会受到干扰,但仍然会收到错误消息)。

如果这是您自己的服务器应用程序,我建议使用以下标题

Content-disposition: attachment; filename=fname.ext

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

只需从javascript调用
window.location.href=new\u url
,它会将浏览器重定向到用户在地址栏中键入的url,我们这样做: 首先添加这个脚本

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

函数populateFrame(id,路径)
{
var ifrm=document.getElementById(id);
ifrm.src=“download.php?path=“+path;
}
将此放置在需要下载按钮的位置(此处我们仅使用一个链接):


文件“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凭据等。

我已经创建了一个开源()(),它也可以帮助解决您的问题。它与iframe的工作原理非常相似,但有一些很酷的特性,我发现它们非常方便:

  • 用户从不会离开启动文件下载的同一页面。这一特性对于现代web应用程序来说正变得至关重要
  • 已测试的跨浏览器支持(包括手机!)
  • 它以类似于jQuery的AJAX API的方式支持POST和GET请求
  • successCallback和failCallback函数允许您明确用户在这两种情况下看到的内容
  • 结合jQuery UI,开发人员可以轻松地显示一个模式,告诉用户正在进行文件下载,在下载开始后解散该模式,甚至友好地通知用户发生了错误。请参阅演示以获取此示例

阅读答案-包括公认的答案,我想指出通过GET将路径直接传递到readfile的安全含义

对某些人来说,这似乎很明显,但有些人可能只是复制/粘贴此代码:

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

那么,如果我将类似“/path/to/fileWithSecrets”的内容传递给这个脚本,会发生什么呢? 给定的脚本将愉快地发送Web服务器用户有权访问的任何文件


请参阅本讨论以了解如何防止这种情况:

当您只需要将浏览器重定向到不同的window.location.href时,为什么要制作服务器端的东西

下面是解析?file=QueryString()并在1秒内将用户重定向到该地址的代码(即使在Android浏览器上也适用于我):


var-urlParams;
(window.onpopstate=函数(){
变量匹配,
pl=/\+/g,//用空格替换加法符号的正则表达式
搜索=/([^&=]+)=?([^&]*)/g,
decode=函数{返回decodeURIComponent(s.replace(pl)(“”);},
query=window.location.search.substring(1);
urlParams={};
while(match=search.exec(查询))
urlParams[decode(匹配[1])]=decode(匹配[2]);
})();
(window.onload=函数(){
var path=urlParams[“文件”];
setTimeout(函数(){document.location.href=path;},1000);
});
如果您的项目中有jQuery,那么一定要删除那些window.onpopstate&window.onload处理程序,并在$(document.ready)(function(){})中执行所有操作

试试这个lib,它比前面描述的所有解决方案都更现代,因为它使用“下载”属性和各种方法的组合来带来最好的体验

在此解释-


似乎是开始用JavaScript下载的理想代码。

要绕过投票结果中的安全漏洞,可以将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>

ForceType应用程序/强制下载
标题集内容处置附件
标题集内容类型应用程序/vnd.android.package
<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>
<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>
<?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']);
         }
     }
?>
<iframe id="download" style="display:none"></iframe>
<input type="submit" value="Download" onclick="ChangeSource('document_path');return false;">
<script type="text/javascript">
    <!--
        function ChangeSource(path){
            document.getElementByID('download').src = 'path_to_php?path=' + document_path;
        }
    -->
</script>