Home > OS >  How to perform actions before providing downloadable files in Vue
How to perform actions before providing downloadable files in Vue

Time:01-14

I want to be able to keep track of file downloads in a Vue project. The goal is to provide a url like mysite.com/some/path/file-name.txt/tracking-source, perform an action like send the path to tracking api, then serve the file at mysite.com/some/path/file-name.txt

I tried using a redirect but it doesn't seem to provide a file download, it just updates the path in the browser.

CodePudding user response:

use a route that captures the "tracking-source" parameter and performs the necessary tracking action, and then serves the file using the sendFile method from the express library.

Here is an example of how to set up a route in a Vue project using the vue-router library:

import Vue from 'vue'
import Router from 'vue-router'
import path from 'path'
import express from 'express'

Vue.use(Router)

const router = new Router({
  routes: [
    {
      path: '/some/path/:fileName/:trackingSource',
      name: 'download-file',
      component: {
        beforeRouteEnter (to, from, next) {
          const { params } = to
          // Perform tracking action using the trackingSource parameter
          // ...
          // Serve the file
          const filePath = path.join(__dirname, 'path/to/files', `${params.fileName}.txt`)
          express.sendFile(filePath, (err) => {
            if (err) next(err)
          })
        }
      }
    }
  ]
})

here the route captures the "fileName" nd "trackingSource" parameters from the URL, and uses the beforeRouteEnter navigation guard to perform the tracking action and serve the file.

without express you can do something like this

<template>
  <div>
    <a ref="downloadLink" :href="fileUrl" download>Download</a>
    <button @click="downloadFile">Download</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      fileUrl: ''
    }
  },
  methods: {
    async downloadFile() {
      const { params } = this.$route
      const fileName = `${params.fileName}.txt`
      const filePath = `/path/to/files/${fileName}`
      const response = await fetch(filePath)
      if (!response.ok) {
        throw new Error(`Failed to fetch file: ${response.status}`)
      }
      const blob = await response.blob()
      this.fileUrl = window.URL.createObjectURL(blob)
      this.$refs.downloadLink.click()
    }
  }
}
</script>

CodePudding user response:

Since I also store my files in the public/files directory of the vue project, I opted to not fetch it.

{
  path: '/files/:fileName/:source', 
  redirect: to => {
    const fileName = to.params.fileName
    logEvent(analytics, fileName, {source: to.params.source});
    const a = document.createElement('a');
    document.body.appendChild(a);

    a.href = `/files/${fileName}`;
    a.download = fileName;
    a.click();
    setTimeout(() => {
      window.URL.revokeObjectURL(a.href);
      document.body.removeChild(a);
    }, 0)
    return {path: '/' }
  }
}
  • Related