[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fi5zFLFt7ursd-ndyUvTEVIqZ-7qaD04pD5-WZjoTlew":3},{"code":4,"message":5,"data":6},200,"成功",{"id":7,"createdAt":8,"title":9,"content":10,"summary":11,"image":12,"uid":13,"user":14,"categoryId":21,"category":22,"subCategoryId":24,"subCategory":25,"comments":27,"status":17,"reason":28,"notice":28,"visitCount":29,"commentCount":30,"keywords":31},128,"2024-10-14T09:58:19.277Z","在web项目中实现一个当前页下载xlsx等文件功能","## 一、背景\n在网页上点击某一个按钮，实现下载文件的操作。\n\n## 二、常见方案\n### 2.1 服务端给一个文件对应的uri\n在前端网页上，使用`a`标签，标签href中设置下载文件对应的链接，一般就可以实现跳转下载文件。\nexample:\n```html\n\u003Ca href=\"https://www.xxx.com/xxx.txt\">下载\u003C/a>\n```\n情况往往没有设定文件下载权限，文件链接暴露的话，任何人都可以下载这个文件。\n如果是`同源`的情况下（网页域名和下载域名符合同源策略），那么一般还比较容易做到鉴权，但实际开发过程中，web服务器和文件服务器是做分离的，所以这种情况是难以做到鉴权的。\n\n### 2.2 使用接口获取文件流方式\n这种方式需要服务端做好配置，读取文件后，并且要设置`response header`，前端请求文件流接口，接口统一封装了对应的token信息。\n#### 2.2.1 前端请求接口\n不管是vue还是react项目，哪怕使用fetch，都是很容易在`request header`中加入对应的authorization信息，服务端就可以使用jwt来判断是否允许下载。\n\n对于使用axios框架来说，更是非常简单的，这里不赘述，咱们重点放在请求后的处理上。\n\n#### 2.2.2 后端读取文件\n这种方式需要服务端做好配置，不仅返回的是文件流，而且要设置对应的`response header`，咱们拿一个xlsx文件类型举例：\n![文件response header](https://image.xinwei.ltd/image1728899316449.png)\n如图所示，服务端要设置一下返回的内容类型：\n```\ncontent-disposition:\nattachment; filename=data_20241014040517.xlsx\n\ncontent-type:\napplication/octet-stream\n\n```\n\n#### 2.2.3 前端读取文件流为blob\n直接上代码。主要逻辑就是使用fetch的`response.blob`方法将流转成blob对象，然后新建一个a标签，模拟点击，实现在当前页面就可以下载文件下来。\n```js\n    const userStore = useUserStoreHook();\n    const token = userStore.accessToken\n    fetch(`${import.meta.env.Fascin8_APP_BASE_URL}/admin/billing/export?` + downloadParam({...param}).substring(1), {\n        method: 'GET',\n        headers: {\n          'Authorization': token,\n        },\n    })\n    .then(response => {\n        console.log(\"请求结果是：\", response)\n        if (!response.ok) {\n          throw new Error('Network response was not ok');\n        }\n        // 从header的Content-Disposition中获取文件名\n        const contentDisposition = response.headers.get('Content-Disposition');\n        let filename = ''\n        if (contentDisposition && contentDisposition.indexOf('attachment') !== -1) {\n            const filenameRegex = /filename[^;=\\n]*=((['\"]).*?\\2|[^;\\n]*)/;\n            const matches = filenameRegex.exec(contentDisposition);\n            if (matches != null && matches[1]) {\n                filename = matches[1].replace(/['\"]/g, ''); // Remove extra quotes\n            }\n        } else {\n          // 自定义一个文件名\n            filename = 'data_' + moment().format(\"YYYYMMDDHHmmss\") + '.xlsx'\n        }\n\n        return response.blob().then(blob => ({ blob, filename }));\n    })\n    .then(({ blob, filename }) => {\n        // 创建a标签，并且触发下载\n        const url = window.URL.createObjectURL(blob);\n        const a = document.createElement('a');\n        a.href = url;\n        a.download = filename;\n        document.body.appendChild(a);\n        a.click();\n        window.URL.revokeObjectURL(url);\n    })\n```\n\n\n\n\n\n\n","我们经常在网页上有下载文件的功能，会有常见的几种方式，这篇文章来和大家分享讨论一下。","https://image.xinwei.ltd/image1728899316449.png",499668042977349,{"phone":15,"userId":13,"nickName":16,"vipType":17,"avatar":18,"sign":19,"createdAt":20},"13121171998","全栈老韩",1,"https://image.xinwei.ltd/images/IMG_5430.JPG","全栈工程师，擅长iOS App开发、前端（vue、react、nuxt、小程序&Taro）开发、Flutter、React Native、后端（midwayjs、golang、express、koa）开发、docker容器、seo优化等。","2024-01-01T16:14:30.305Z",2,{"id":21,"name":23},"IT技术",32,{"id":24,"name":26,"parentId":21},"前端",[],"",281,0,"web dowload,web下载文件,vue xlsx,vue下载表格,vue"]