首页 > 解决方案 > 为什么来自锚元素的 http 请求不能正常工作?

问题描述

假设我有一个 HTML 页面,可以在 http://example.com/redir.php?id=someID.

我希望一些 JavaScript 在执行时触发下载http://example.com/?file=hgc56gjyd,并使用名称传递它newFileName

这是我的尝试:

var c = document.createElement("a");
c.href = "?file=hgc56gjyd";
c.download = "newFileName";
c.click();

但是当我执行这个时,实际的请求是http://example.com/redir.php?file=hgc56gjyd. 注意添加的'redir.php'. 此 URL 不正确,下载的文件为空。但在这种情况下,更改名称有效。

如果我写出整个 URL 或使用“/”或“./”(开头):

c.href = "http://example.com/?file=hgc56gjyd"
c.href = "/?file=hgc56gjyd"
c.href = "./?file=hgc56gjyd"

那么该download属性不会生效,因此生成的文件不会重命名为newFileName.

如何在获得以下功能的同时触发对正确 URI 的请求download

这是在 Chrome 中。

标签: javascript

解决方案


通过做这个:

c.href = "?file=hgc56gjyd";

在 的文档中http://example.com/redir.php?id=someID,您实际上是在执行以下操作:

c.href = "http://example.com/redir.php?file=hgc56gjyd";
// Note ---------------------^^^^^^^^^

如果你想要它http://example.com/?file=hgc56gjyd,你不必使用绝对路径——站点的绝对路径是坏事™ :-)——只需使用前导/或前导./

c.href = "/?file=hgc56gjyd";
// -------^

或者

c.href = "./?file=hgc56gjyd";
// -------^^

/如果您想进入域根目录而不管链接所在页面的 URL,请使用前者 ( );./如果您只想要链接所在页面级别的默认 URL,请使用后者 ( )。在您的示例中,这些是同一件事,但如果这是在http://example.com/somethinghere/redir.php?id=someID.

现场示例(用https://stacksnippets.net代替http://example.comjs代替redir.php):

console.log("Location: " + location);

// Your current code
var c = document.createElement("a");
c.href = "?file=hgc56gjyd";
c.download = "newFileName";
// Notice the `js` on this (which is like your redir.php)
console.log("Your Link:" + c.href);

// The corrected code (#1
var c2 = document.createElement("a");
c2.href = "/?file=hgc56gjyd";
c2.download = "newFileName";
// Notice there's no `js` now
console.log("Fix1:     " + c2.href);

// The corrected code (#2
var c3 = document.createElement("a");
c3.href = "./?file=hgc56gjyd";
c3.download = "newFileName";
// Notice there's no `js` now
console.log("Fix2:     " + c3.href);
.as-console-wrapper {
  max-height: 100% !important;
}

在问题和评论中,您说过仅"?file=hgc56gjyd"在重命名文件方面使用“有效”(但不下载它,您会得到一个空文件)但使用正确的路径(绝对路径或其中一个上面的解决方案)“不起作用”,因为它使用来自服务器的名称。但不同之处不在于路径本身,而在于服务器为响应路径而发送的内容:Content-Disposition标头的文件名胜过您放入download属性中的任何内容。实际的下载脚本 (at /) 发送它。显然redir.php脚本没有。

解决方案是将文件名方面转移到server,确保返回带有此标头的文档:

Content-Disposition: attachment; filename=newFileName

例如,如果下载是由 PHP 代码处理的,你会这样做:

<!-- language: lang-php -->

<?php
header("Content-Disposition: attachment; filename=newFileName")

这样,服务器就会告诉浏览器名称应该是什么。仍然允许浏览器忽略它(但以我的经验通常使用它)。

如果所有这些都失败了(例如,因为您无法更改发送该响应的服务器代码),还有一个选择:使用BlobURL.createObjectURL(这令人惊讶地得到很好的支持)。这是一个使用示例fetch

// In an event handler...
fetch("./?file=hgc56gjyd")
    .then(response => {
        if (!response.ok) {
            throw new Error("HTTP error " + response.status);
        }
        return response.blob();
    })
    .then(blob => {
        var c = document.createElement("a");
        c.href = URL.createObjectURL(blob);
        c.download = "bar.json";
        c.click();
    })
    .catch(e => {
        alert(e.message);
    });

那成功地使用了这个download名字。

如果你做不到恐怕我认为你真的不走运。:-|


推荐阅读