给你说个gitee都没处理的下载白屏优化

174 阅读2分钟

讲解在同名B/D上都有,主要介绍一些跟业务无关的代码技巧

注: 在CodeReview中,部分内容主观性较大,一家之言姑且听之

本文主要介绍下载出现的"白屏"问题

业务抽象

后端代码

const express = require('express');
const app = express();
const xlsx = require('node-xlsx').default;
app.use(express.static('public'))

const api = () => new Promise(resolve => {
    setTimeout(() => {
        resolve([
            ['zh-CN', 'en'],
        ])
    }, 5000)
});
// 动态生成excel/pdf/world/压缩包等资源文件
app.get('/excel', async (req, res) => {
    res.setHeader('Content-Type', 'application/xlsx')
    res.setHeader('Content-Disposition', 'attachment;filename=demo.xlsx')
    // 模拟 获取数据
    const data = await api();
    const buffer = xlsx.build([{ name: 'mySheetName', data: data }]);
    res.send(buffer); // json格式
})
app.listen(3000, () => { })

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button onclick="window.location.href='/excel'">下载1</button>
    <button onclick="download()">下载2</button>
    <script>
        function download() {
            window.open("/excel")
        }
    </script>
</body>
</html>

交互问题

href切换

动画.gif

浏览器输入框后回车,与ajax是不一样的,浏览器输入地址类型属于document,是三个documentLoader切换的过程,第一个documentLoader做拦截,第二个documentLoader发送请求,此时浏览器左上角进入loading状态,但可以随时取消,接收到数据后,才会进入到第三个documentLoader,根据content-type决定"渲染器",这里指进入到下载器里面

window.open 新建

动画.gif

遇上一个问题原理一样,但因为不需要渲染,最后会自动关闭

解决方案

flushHeader

先介绍正经的方案,flushHeader提前发送header即可

const express = require('express');
const app = express();
const xlsx = require('node-xlsx').default;
app.use(express.static('public'))

const api = () => new Promise(resolve => {
    setTimeout(() => {
        resolve([
            ['zh-CN', 'en'],
        ])
    }, 5000)
});
// 动态生成excel/pdf/world/压缩包等资源文件
app.get('/excel', async (req, res) => {
    res.setHeader('Content-Type', 'application/xlsx')
    res.setHeader('Content-Disposition', 'attachment;filename=demo.xlsx')
    res.flushHeaders()
    // 模拟 获取数据
    const data = await api();
    const buffer = xlsx.build([{ name: 'mySheetName', data: data }]);
    res.end(buffer);
})
app.listen(3000, () => { })

href

动画.gif

点击以后直接进入到下载流

window.open

动画.gif

同上,好一点,但不多,第7下还是会出现,但跟这个方案没有关系,猜下原因 /dog

原因

服务器响应时间

image.png

document的解析原理上面已经提到过了,里面包含一个关键数据content-type,只要能够提前获取到就可以了

当前的主流方案是send,即准备好数据后,再统一发送,所以中间的5s属于服务响应时间

资源下载时间

image.png

接收到content-type后,可以理解为接收到第一个字节后,可以选择"解析器",此时进入到内容下载阶段,当然,上图看不出来,因为后面的内容托管给下载器了,下面这个图可以看出来,我用fetch加载的

image.png

方案2

硬控

纯前端没有太好的办法,此时会硬控,比如gitee的下载提示,原因如下

微信截图_20240528173558.png

没好意思下几百M的,注意服务器等待时间 微信截图_20240528173646.png