在web项目中实现一个当前页下载xlsx等文件功能

原创文章
声明:作者声明此文章为原创,未经作者同意,请勿转载,若转载,务必注明本站出处,本平台保留追究侵权法律责任的权利。
全栈老韩
全栈工程师,擅长iOS App开发、前端(vue、react、nuxt、小程序&Taro)开发、Flutter、React Native、后端(midwayjs、golang、express、koa)开发、docker容器、seo优化等。

一、背景

在网页上点击某一个按钮,实现下载文件的操作。

二、常见方案

2.1 服务端给一个文件对应的uri

在前端网页上,使用a标签,标签href中设置下载文件对应的链接,一般就可以实现跳转下载文件。
example:

html 复制代码
<a href="https://www.xxx.com/xxx.txt">下载</a>

情况往往没有设定文件下载权限,文件链接暴露的话,任何人都可以下载这个文件。
如果是同源的情况下(网页域名和下载域名符合同源策略),那么一般还比较容易做到鉴权,但实际开发过程中,web服务器和文件服务器是做分离的,所以这种情况是难以做到鉴权的。

2.2 使用接口获取文件流方式

这种方式需要服务端做好配置,读取文件后,并且要设置response header,前端请求文件流接口,接口统一封装了对应的token信息。

2.2.1 前端请求接口

不管是vue还是react项目,哪怕使用fetch,都是很容易在request header中加入对应的authorization信息,服务端就可以使用jwt来判断是否允许下载。

对于使用axios框架来说,更是非常简单的,这里不赘述,咱们重点放在请求后的处理上。

2.2.2 后端读取文件

这种方式需要服务端做好配置,不仅返回的是文件流,而且要设置对应的response header,咱们拿一个xlsx文件类型举例:
文件response header
如图所示,服务端要设置一下返回的内容类型:

复制代码
content-disposition:
attachment; filename=data_20241014040517.xlsx

content-type:
application/octet-stream

2.2.3 前端读取文件流为blob

直接上代码。主要逻辑就是使用fetch的response.blob方法将流转成blob对象,然后新建一个a标签,模拟点击,实现在当前页面就可以下载文件下来。

js 复制代码
    const userStore = useUserStoreHook();
    const token = userStore.accessToken
    fetch(`${import.meta.env.Fascin8_APP_BASE_URL}/admin/billing/export?` + downloadParam({...param}).substring(1), {
        method: 'GET',
        headers: {
          'Authorization': token,
        },
    })
    .then(response => {
        console.log("请求结果是:", response)
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        // 从header的Content-Disposition中获取文件名
        const contentDisposition = response.headers.get('Content-Disposition');
        let filename = ''
        if (contentDisposition && contentDisposition.indexOf('attachment') !== -1) {
            const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
            const matches = filenameRegex.exec(contentDisposition);
            if (matches != null && matches[1]) {
                filename = matches[1].replace(/['"]/g, ''); // Remove extra quotes
            }
        } else {
          // 自定义一个文件名
            filename = 'data_' + moment().format("YYYYMMDDHHmmss") + '.xlsx'
        }

        return response.blob().then(blob => ({ blob, filename }));
    })
    .then(({ blob, filename }) => {
        // 创建a标签,并且触发下载
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        window.URL.revokeObjectURL(url);
    })

暂无评论,快来发表第一条评论吧