54

This question is different from the other answer provided, because my question is focused on VUE and if VUE also has a way to prevent the default method.

This question is more specific to HTML 5 "download" along with VUE binding of the :href and why it doesn't work to prevent the default browser behavior of opening the file in a new tab.

Expected behavior : Download the file to the browser

Actual behavior : Opens the file in a new tab

Exception: Only images, pdf and browser compatible files are opened in a new tab, other files like .exe are downloaded as normal - Why is this, can this behavior be changed in html?

Adding target="_blank" does not solve the problem

<a :href="downloadById(item.url)" download>Download</a>

When the above link is clicked, the file is opened in a new browser tab, i need to prevent this default behavior and force a download upon click. The HTML 5 tag "download" is suppose to solve this problem doesn't seem to work.

Chrome has recently deprecated the download tag form working with cross domain downloads. Does vue have a modifier to prevent this default? Are there any other ways to download the file either in javascript or in html?

One proposed solution is to read the URL as a arrayBuffer and then create a new blob in the DOM, and then create an anchor element and click it.. But that seems hacky to force a download of a file.

I am sure their must be a cleaner solution to download a file form a URL, its a trivial problem, hoping for a simple solution.

Thanks.

Ricky-U
  • 592
  • 1
  • 4
  • 11
  • this is more related with your server code than vue itself. try to add the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition Content-Disposition header on server-side so you get backed by official specs rather than proprietary markup extensions. – Sombriks Dec 14 '18 at 01:29
  • The url is a public url that contains a image, an example would be a googledrive public link image. sadly I dont have control over the server and its headers, i only want the user to download the file to the browser without it opening in a new tab. – Ricky-U Dec 14 '18 at 01:31
  • AFAIK, the only current solution available is the "hacky" one you mentioned. – tony19 Dec 14 '18 at 04:34
  • Possible duplicate of [Any fallback client-side solutions for the html5 download attribute?](https://stackoverflow.com/questions/18652750/any-fallback-client-side-solutions-for-the-html5-download-attribute) – tony19 Dec 14 '18 at 04:34
  • HI Tony, thanks, yes i was also looking for a vue specific example, since vue has many methods to prevent default, i was hoping that it did.. but it doesn't in this case and the below answer by lars solve it for me. – Ricky-U Dec 14 '18 at 11:34

4 Answers4

140

You can fetch the file as a blob and provide it the same way, there will be no request that leads into CORS issues.

Template

<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)
  }
}

Notes: I used Axios for my example but that's not a requirement, the blob's mime type is hardwired for the sake of simplicity.

Lars Beck
  • 3,446
  • 2
  • 22
  • 23
  • Thank you, your answer solved my question because you used VUE vs the other answers did not. – Ricky-U Dec 14 '18 at 11:32
  • @Ricky-U If this answer solved your problem then please consider accepting the answer (by clicking the green tick) and / or upvoting. Thanks! – JustCarty Dec 17 '18 at 10:20
  • 2
    It leads to cors problem – Mahmud hasan Jul 11 '20 at 11:22
  • i have cors problem too – Hachimi Ahmed Amine Jul 23 '20 at 08:28
  • 2
    If you have CORS issues, make sure you have permissions to access the source using JavaScript. Refer to: https://developer.mozilla.org/nl/docs/Web/HTTP/CORS – Chris van Chip Aug 04 '20 at 19:04
  • 2
    If you're willing to use external libraries, [file-saver](https://www.npmjs.com/package/file-saver) removes the need to create a link element. Just import with `import { saveAs } from 'file-saver';` and call with `saveAs(blob, "filename.ext");` – AvahW Sep 03 '20 at 16:41
  • 7
    This approach is bad practice for large file because it loads the entire file to browser's memory – Eido95 Aug 31 '21 at 09:05
  • It downloads only 122bytes of filws – ash Oct 27 '22 at 04:29
  • @Eido95 what should be the ideal approach then? – Walnussbär Mar 03 '23 at 13:43
  • @Walnussbär use a workaround similar to this (doesn't load the file to browser's memory): https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743 – Eido95 Mar 07 '23 at 07:48
  • @Eido95 which workaround do you mean? As soon as I write `new Blob()`, the data must be in the browsers memory. Correct me if I'm wrong about this – Walnussbär Mar 15 '23 at 09:51
  • @Walnussbär, this works for me: https://gist.github.com/Eido95/0517140511a1ee77e4a641a48a157a36 – Eido95 Mar 19 '23 at 13:44
7

I'm using Laravel and Vue. With https://github.com/maatwebsite/Laravel-Excel

In Laravel route

In the controller method for download, I return https://docs.laravel-excel.com/3.1/exports/exportables.html -> responsible instance

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

In my 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>
Emeka Mbah
  • 16,745
  • 10
  • 77
  • 96
4

If you want the browser to handle the download instead of handling the download in JavaScript you need to use window.open:

window.open("<insert URL here>")

This gives a better user experience IMO but gets tricky to set up when trying to access authorization-protected content. To do this you need to store the authorization in cookies instead of relying on storing the authorization header in the browser's local storage. This will require configuring your server & client to authenticate this way.

To do this client side make sure axios knows to store credentials:

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

To do this server side depends on which server you use.

HyperActive
  • 1,129
  • 12
  • 12
  • Adding that last line I am having problems with CORS. – inane Feb 11 '21 at 09:28
  • 2
    Not bad, but there is an ugly blink when using this (probably because a new window is opening then closing quickly). – kissu Apr 15 '21 at 17:44
  • Please help @kissu In my case it is simply opening that link in a new browser or in any ways whenever I am trying to initiate a download with a href also. It is doing the same. Just playing the video and not starting the download process. – ash Oct 27 '22 at 04:38
  • 1
    @ash give a try to that one: https://stackoverflow.com/a/67118753/8816585 – kissu Oct 27 '22 at 07:12
3

In addition to the accepted answer, files can be easily downloaded through the file-saver package.

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

Then:

import {saveAs} from 'file-saver';
downloadItem(url) {
    axios
        .get(url, {responseType: 'blob'})
        .then(response => {
            saveAs(response.data, 'downloaded-file.pdf');
        })
}
Nathan Wailes
  • 9,872
  • 7
  • 57
  • 95
Tobias Ernst
  • 4,214
  • 1
  • 32
  • 30