Vue/HTML/JS如何使用下载标签将文件下载到浏览器?

55

这个问题与其他提供的答案不同,因为我的问题着重于VUE,是否也有一种方法可以防止默认方法。

这个问题更具体地涉及到HTML 5中的“下载”和VUE绑定的 :href,以及为什么它不能防止浏览器打开文件的默认行为。

预期行为:将文件下载到浏览器中

实际行为:在新标签页中打开文件

异常情况:只有图片、pdf和浏览器兼容的文件才会在新标签页中打开,其他文件如.exe则会像正常下载一样被下载。为什么会这样,能否在html中改变这种行为?

添加target="_blank"并不能解决这个问题。

<a :href="downloadById(item.url)" download>Download</a>
当点击上述链接时,文件将在新的浏览器标签中打开,我需要阻止这种默认行为并在单击时强制下载。HTML 5标签“download”应该解决此问题,但似乎不起作用。
最近,Chrome已经废弃了使用跨域下载的下载标签功能。Vue是否有修改器可以防止此默认行为?是否有任何其他方法可以通过JavaScript或HTML下载文件?
一种提议的解决方案是将URL读取为arrayBuffer,然后在DOM中创建一个新的blob,然后创建一个锚元素并单击它。但这似乎是一种hacky的方式来强制下载文件。
我相信必须有一种更简洁的解决方案来从URL下载文件,这是一个微不足道的问题,希望有一个简单的解决方案。
谢谢。

这与Vue本身无关,更多地涉及到您的服务器代码。建议在服务器端添加https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition Content-Disposition头部,以便您能够符合官方规范而非专有标记扩展。 - Sombriks
该URL是一个公共URL,其中包含一张图片,例如GoogleDrive的公共链接图片。不幸的是,我无法控制服务器和其标头,我只想让用户在不打开新选项卡的情况下将文件下载到浏览器中。 - Ricky-U
目前就我所知,唯一可用的解决方案就是你提到的“hacky”方法。 - tony19
嗨Tony,谢谢,是的,我也在寻找一个Vue特定的例子,因为Vue有许多方法来防止默认行为,我希望它可以...但在这种情况下它不行,下面lars的答案解决了我的问题。 - Ricky-U
4个回答

140
您可以将文件作为Blob获取并以相同的方式提供它,这样就不会出现导致CORS问题的请求。
模板
<a
  :href="item.url"
  v-text="item.label"
  @click.prevent="downloadItem(item)" />

Vue(中文名:视图)
methods: {
  downloadItem ({ url, label }) {
    Axios.get(url, { responseType: 'blob' })
      .then(response => {
        const blob = new Blob([response.data], { type: 'application/pdf' })
        const link = document.createElement('a')
        link.href = URL.createObjectURL(blob)
        link.download = label
        link.click()
        URL.revokeObjectURL(link.href)
      }).catch(console.error)
  }
}

注:我在示例中使用了 Axios,但这不是必需的,为了简单起见,blob 的 MIME 类型被硬编码。

谢谢,您的答案解决了我的问题,因为您使用了VUE,而其他答案没有。 - Ricky-U
2
它导致了跨域问题。 - Mahmud hasan
2
如果您遇到CORS问题,请确保您有使用JavaScript访问源的权限。请参考: https://developer.mozilla.org/nl/docs/Web/HTTP/CORS - Chris van Chip
2
如果你愿意使用外部库,file-saver 可以避免创建链接元素的需要。只需使用 import { saveAs } from 'file-saver'; 导入,然后使用 saveAs(blob, "filename.ext"); 调用即可。 - sonrad10
7
这种方法不适合处理大型文件,因为它会将整个文件加载到浏览器的内存中。 - Eido95
显示剩余7条评论

7

我正在使用Laravel和Vue。

https://github.com/maatwebsite/Laravel-Excel

在Laravel路由中

在下载的控制器方法中,我返回https://docs.laravel-excel.com/3.1/exports/exportables.html -> 负责实例。

Route::get('users/download', 'userController@download')
->name('users.download');

在我的Vue中:
<!--
  - Page Header Template
  -->
<template>
    <div class="page-header">
        <button 
            v-if="$is_admin"
            class="button secondary download" @click="download">Download
        </button>
    </div>
</template>

<script>
    export default {

        methods: {
            download () {
                const url = '/users/download';
                window.location.href = url;
            }
        },
    };
</script>

4
如果你希望浏览器来处理下载而不是在JavaScript中处理下载,那么你需要使用window.open:
window.open("<insert URL here>")

在我看来,这样可以提供更好的用户体验,但在尝试访问受授权保护的内容时设置起来会比较棘手。为此,您需要将授权存储在cookie中,而不是依赖于存储在浏览器本地存储中的授权头。这将需要配置您的服务器和客户端以通过此方式进行身份验证。

为此,请确保axios知道存储凭据:

import axios from 'axios'
axios.defaults.withCredentials = true

要在服务器端完成这个任务,取决于你使用的服务器。


添加最后一行时,我遇到了CORS的问题。 - inane
2
还不错,但是使用时会出现一个丑陋的闪烁(可能是因为一个新窗口正在快速打开然后关闭)。 - kissu
请帮助@kissu,在我的情况下,只需在新浏览器中打开该链接或以任何方式尝试使用href启动下载时,它都会执行相同的操作。只是播放视频而不是开始下载过程。 - ash
1
@ash 试试这个:https://dev59.com/KW865IYBdhLWcg3wOcHy#67118753 - kissu

3

除了被接受的答案外,可以通过file-saver软件包轻松下载文件。

npm install file-saver --save
npm install @types/file-saver --save

接下来:

import {saveAs} from 'file-saver';
downloadItem(url) {
    axios
        .get(url, {responseType: 'blob'})
        .then(response => {
            saveAs(response.data, 'downloaded-file.pdf');
        })
}

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