【问题标题】:Set file name and format if file or file location contains neither如果文件或文件位置都不包含,则设置文件名和格式
【发布时间】:2018-01-01 04:13:26
【问题描述】:

我想设置一个“非典型文件”的文件名和格式,因为缺少更好的词。文件存储在以下位置...

let link = https://.../videoplayback?ipbits=0&initcwndb...D134213BA9465CB74DFD36CDE47BF.102638C4A9F3ACA357F79EE747DD5F49F1E0F0DE

当人们下载此类文件时,即使它们是完全不同的文件,它们最终总是以相同的名称保存,而不是文件扩展名。

我试过url.download = link + '.mp4',但这没有任何效果。

我该怎么做?

【问题讨论】:

  • 你是如何获取文件的?
  • @TheChetan 通过浏览器从互联网上下载它们
  • @Anthony 文件是否带有 CORS 标头?
  • @guest271314 我不知道。我怎么会确定呢? ajax 什么的?
  • @Anthony 你对文件有控制权吗?

标签: javascript html file download


【解决方案1】:

根据具有 download 属性的 anchor tags 的 MDN:

可以与 blob: URLs 和 data: URLs 一起使用,方便用户使用 下载以编程方式生成的内容 JavaScript(例如,使用在线绘图 Web 应用创建的图片)。

如果 HTTP 标头 Content-Disposition: 存在并给出 与此属性不同的文件名,HTTP 标头具有优先权 在这个属性上。

如果此属性存在且 Content-Disposition: 设置为 内联,Firefox 优先考虑 Content-Disposition,例如 文件名大小写,而 Chrome 优先考虑下载属性。

此属性仅适用于带有的资源链接 同源。

因此,如果您动态生成这些链接并且它们来自您自己的服务器 - 您可以设置文件名。 跨域请求不起作用!

您也许可以使用 ajax 将文件作为 blob 获取,并诱使浏览器认为数据不是跨域的。请参阅this answer 了解一种可能的实现方式。

【讨论】:

  • 在 localhost / jsfiddle 上尝试这个 - 单独下载 attr 不起作用。我认为这与 localhost 不充当服务器有关。可能在 apache 上工作,但我不想这样做,以防你在本地设置它。这个解决方法虽然修复了它:gist.github.com/nelsonkhan/7d4e4b5c2f6898cf6a40bc12a236cbbb
  • 以上内容很可能适用于较新的浏览器,但 IE 支持是一个已知问题。我不确定移动浏览器 - 我在任何地方都找不到兼容性信息...
  • 我只需要这个就可以在 Chrome 上工作。它永远是跨域的。我不确定您链接的那篇文章如何描述如何欺骗浏览器假设跨域是同源的??
【解决方案2】:

一种方法是在设置<a> 元素的.download 属性之前执行HEAD 请求以确定所请求资源的Content-Type,使用JSON 字符串、JavaScript 对象或其他键、值配对数据存储格式以将有效MIME 类型对反映到与MIME 类型属性对应的有效文件扩展名。使用.indexOf()RegExp或其他过滤方法来判断字符串或​​对象的属性名与属性值是否匹配,如果为真则存储扩展值并将扩展连接到建议的文件名.

如果文件未使用 CORS 标头提供,您可以尝试使用代理发出 GETHEAD 请求以获取 Content-Type 标头,然后再设置 .download 属性。

应该注意.download 属性只是给用户的文件名建议。用户可以随时更改文件的名称,包括删除文件扩展名,无论出于何种原因,他们可能有也可能没有。或者根本不下载资源。

const mimeTypeExtensions = {
  "text/plain": ".txt",
  "video/mp4": ".mp4",
  /* valid MIME types and corresponding extensions */
}

const a = document.querySelector("a");

a.addEventListener("click", event => {

  if (!a.download) {
  
    event.preventDefault();
    
    fetch(a.href, {method: "HEAD"})
    .then(response => {
      const mimeType = response.headers.get("content-type");
      let fileExtension = "";
      for (let [key, prop] of Object.entries(mimeTypeExtensions)) {
          if (key.indexOf(mimeType) > -1) {
            fileExtension = prop;
            break;
          };
       }
       a.download = `filename${fileExtension}`;
       a.click();
    })
    .catch(err => console.error(err))

  }

});
<a href="https://gist.githubusercontent.com/guest271314/11edc4566ba94f204dd46e6ae26edaad/raw/d118e99abbe2a2c60634e46816df9e1b9de6b6b8/D134213BA9465CB74DFD36CDE47BF102638C4A9F3ACA357F79EE747DD5F49F1E0F0DE">click</a>

【讨论】:

  • 当我有时间时,我将不得不重读这篇文章。您能否详细说明“...使用代理发出HEADGET 请求...”。我想我明白你在说什么,但措辞很难理解
  • @Anthony 可以使用YQL请求文件,然后解析响应的Content-Type,或者使用响应本身设置<a>元素.hrefDirectly download img tag , 其中data URIMIME 类型部分可以被解析以在答案中执行相同的过程以设置文件扩展名建议。
【解决方案3】:

如果您不介意添加库,这是一个简单的选择。 FileSaver.js 很好地处理了这些情况,同时隐藏了丑陋的细节。我将介绍如何创建 Blob,将下载的内容写入其中,然后使用从 Blob 创建的对象 URL 创建一个锚标记,但 FileSaver 已经处理了,如果你可以查看源代码真的很想要。

只需修改以下内容以添加一些用于确定文件名/文件类型的检查和逻辑,您应该能够处理您的用例。

<script src="cdn-to-FileSaver.js"></script>

<script>
var url = ...;
fetch(url).then((response) => {
  var blob = response.blob();
  var filename = 'song.mp4';

  saveAs(blob, filename);
});
</script>

【讨论】:

  • 我有空的时候会去看看
猜你喜欢
  • 1970-01-01
  • 2010-11-16
  • 1970-01-01
  • 2013-08-10
  • 1970-01-01
  • 1970-01-01
  • 2015-08-02
  • 1970-01-01
  • 2011-02-21
相关资源
最近更新 更多