全栈笔记

几种常见下载方式总结&html2canvas截图相关

2020-04-23  本文已影响0人  韭菜的故事

下载方法一

window.open('https://xxx.xxx');

最简单的下载方式,表现为新开窗口访问服务器上的文件执行下载。

  1. 只支持get方法,而且参数太长可能会出现问题。

  2. 浏览器新开窗口开始下载后tab页会瞬间关闭,可能交互不太友好,这个看设计要求了。

下载方法二

使用form表单提交。基本流程为创建form元素,在form内创建隐藏的input元素,input的name属性为下载接口传参的字段名,value属性为字段对应的值。form的method属性代表接口请求方式(一般是get或post),action属性为接口地址。最后调用form元素的submit方法执行下载

可以动态创建form和input元素

// 需要传给后台的参数
const downloadObj = {
    name: 'a',
    value: 1
};
const downLoadForm = document.createElement('form');
document.body.appendChild(downLoadForm);
Object.keys(downloadObj).forEach(key => {
    const input = document.createElement('input');
    input.type = 'hidden';
    input.name = key;
    input.value = downloadObj[key];
    downLoadForm.appendChild(input);
});
downLoadForm.method = 'get'; // get/post
downLoadForm.action = 'https://xxxx.xxxx';
downLoadForm.submit();
document.body.removeChild(downLoadForm);

这种方法的优点是支持post方法下载,同时下载过程不会新开窗口。不过如果下载文件过大或者接口响应过慢可能会导致用户点了下载之后不仔细看会觉得发现页面没什么变化...(仔细看的话左下角会有一个浏览器自带的提示文字)

我之前做过的下载基本都是用的以上两种方式,而且纵观一些大型网站的下载基本也是以上两种居多。直到最近做的一个项目已经不适合使用上面两种方式了。需求是这样的,用户点击下载,前端生成页面内容截图(因为有很多echarts图表)发给后台,后台生成pdf返回给前端下载。首先传图片给后台的话使用的是文件流,其次这次是和兄弟部门合作开发,后台服务器不保存文件(有些历史原因),只返回文件流给前端。由这两点导致以上两种下载方法都没法使用了。于是我综合网络各种前辈资料总结出了第三种下载方法:

下载方法三

流程是使用常规的接口请求拿到后台返回的文件流,由前端通过Blob对象转化为所需文件,使用动态创建a标签并执行点击事件完成下载

直接上代码(使用vue+axios)


// import {postPDFDownData} from 'api.js';
// element的loading
const loading = this.$loading({
    lock: true,
    text: '拼命下载中...',
    spinner: 'el-icon-loading'
});
// ...
// 使用封装好的方法获得图片文件对象
const imgFile = convertBase64UrlToFile(base64);
const formData = new FormData();
formData.append('img', imgFile);
formData.append('fileName', 'report');
// 传给后台的是formData对象
postPDFDownData(formData)
    .then(res => {
        let blob = new Blob([res], { type: 'application/pdf' }); // 根据要求转化为不同的二进制对象
        let downloadElement = document.createElement('a');
        let href = window.URL.createObjectURL(blob); //创建下载的链接
        downloadElement.href = href;
        let timestamp = new Date().getTime();
        downloadElement.download = timestamp + '.pdf';
        document.body.appendChild(downloadElement);
        downloadElement.click(); //点击下载
        document.body.removeChild(downloadElement); //下载完成移除元素
        window.URL.revokeObjectURL(href); //释放掉blob对象
    })
    .catch(() => {
        this.$message('暂无数据,请稍后重试');
    })
    .finally(() => {
        loading.close();
    });
// ----------------------------------
// request.js 将axios封装了下
// api.js 下载pdf的方法
export function postPDFDownData(data) {
    return request({
        url: `xxxx/xxx`,
        method: 'post',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, // 根据接口要求设置请求头
        responseType: 'blob', // 设置返回类型,必须
        data: data
    });
}

这种下载方式优点很明显,过程可以控制,体验较好,但是下载的文件过大可能等待的时间会比较长(下载文件流加上转换),而页面可能整体都在转圈,会导致用户无法进行其他操作。

html2canvas生成页面截图踩坑

介绍:html2canvas能够实现在用户浏览器端直接对整个或部分页面进行截屏,大致的原理是通过读取DOM将页面所需的内容渲染成canvas元素。

前排提醒:高版本的html2canvas截图可能存在bug,我在项目中使用的版本是0.5.0-alpha1

正是通过这个插件我才能够将页面截图下来传给后台。当然,一系列问题也就随之而来...

关键代码如下

export function initImage(id, boxid, cb) {
    const originNode = document.getElementById(id); // 截图区域
    const boxNode = document.getElementById(boxid); // 截图区的父节点
    boxNode.removeChild(originNode); // “移花接木”
    document.body.appendChild(originNode);
    html2canvas(originNode).then(canvas => {
        document.body.removeChild(originNode); // 还原
        boxNode.appendChild(originNode);
        return cb(canvas.toDataURL());
    });
}

// 调用
initImage('reportPart', 'reortPartBox', base64 => {
 // 里面就是上文写的下载的代码
}

最后代码长这样(当然,时间仓促,方法的通用性几乎没有,凑合用了)

export function initImage(id, boxid, cb) {
    const originNode = document.getElementById(id);
    const boxNode = document.getElementById(boxid);
    const chartBoxDOM = document.querySelectorAll('.chart-component');
    // 解决表格出现滚动条时截图出现的问题
    chartBoxDOM.forEach(v => {
        v.style.overflow = 'hidden';
    })
    boxNode.removeChild(originNode);
    document.body.appendChild(originNode);
    html2canvas(originNode).then(canvas => {
        chartBoxDOM.forEach(v => {
            v.style.overflow = 'auto';
        })
        document.body.removeChild(originNode);
        boxNode.appendChild(originNode);
        return cb(canvas.toDataURL());
    });
}

html2canvas还有些其他的坑,由于我的项目没有涉及到,所以没有研究,总之有问题看issues大半都能得到解决,剩下的。。办法也都是人想出来的,不是吗。

最后,关于下载方式的选择还是得看需求和后台的情况而定。目前我觉得也没有一个最优的解决方案。

上一篇 下一篇

猜你喜欢

热点阅读