天天看点

导出功能的体验没做好,差点被投诉了

背景:

新上线的一个功能,大家都做得比较辛苦。

同时,也收到用户反馈功能不好用。

问题描述:

反馈的问题是,在导出的文件名看不懂,一旦导出多了,不好找。如果下载一个用户再自己重新命名一下,又会影响效率。

导出功能的体验没做好,差点被投诉了

根据文件名不知道里面的内容

这就很烦了,

不改下名,不好找导出的文件。

改吧,又太麻烦。

用户又能拿这个功能怎么样,只能吐槽这个新功能不好用罢了

这是一个非功能的体验问题。

直接原因:

没有指定下载文件名,浏览器使用了默认的命名策略:将url上的非法字符去掉,然后拼一下。如果得到的字符串太长,还会进行截断处理。

原因分析:

用户执行导出后,后端返回的是一个包含了导出内容的oss地址,也就是一个Url。前端直接把这个url放到<a>标签中。用户点击进行下载

导出功能的体验没做好,差点被投诉了

下载时的交互

这种情况下,浏览器下载时展示在状态栏上的名字,浏览器就自由发挥了,目前浏览器的命名规则是将url上的非法字符去掉,然后拼一下。

导出功能的体验没做好,差点被投诉了

下载的文件名

优化意见:

方案1:服务器返回导出内容的二进制流。由服务器指定浏览器导出的文件名。

代码实现:

/**
 * 将二进制流写入HttpServletResponse
 *
 * @param fileName 浏览器下载时弹出下载框、展示在状态栏中的文件名
 * @param response 承载返回给浏览器的对象
 * @param workbook POI中定义的excel。承载了excel模型及存放的数据
 * @throws IOException
 */
private static void downLoadExcelWithOutputStream(HttpServletResponse response, Workbook workbook, String fileName) throws IOException {
    response.setCharacterEncoding(StandardCharsets.UTF_8.name());
    response.setHeader("content-Type", "application/vnd.ms-excel");
    /**
     * 如果文件名是包含中文字符,需要进行编码。否则,浏览器下载时文件名会是乱码
     */
    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
    ServletOutputStream servletOutputStream = response.getOutputStream();
    workbook.write(servletOutputStream);
     /**
     * 关闭writer,释放内存
     */ 
    workbook.close();
    servletOutputStream.flush();
    /**
     * 此处记得关闭输出Servlet流
     */
    IoUtil.close(servletOutputStream);
}           
导出功能的体验没做好,差点被投诉了

关键代码

方案2:服务器返回存放下载文件的oss Url,前端指定浏览器下载文件的文件名。

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>前端自定义下载文件名e</title>

</head>

<body>

待下载的文件:<input type="text" name="fileUrl" id="fileUrl" value='' ><br />

自定义文件名:<input type="text" name="fileName" id="fileName" value=''><br/>

<button id='btn'>下载</button><br />

<span id='status'></span><br />

</body>

<script>

var url=document.getElementById('fileUrl').value;

console.log(url);

document.getElementById('btn').onclick = function() {

document.getElementById('status').innerHTML = '下载中';

fetch(url).then(res => res.blob().then(blob => {

var a = document.createElement('a');

var url = window.URL.createObjectURL(blob);

a.href = url;

a.download = document.getElementById('fileName').value;

a.click();

window.URL.revokeObjectURL(url);

document.getElementById('status').innerHTML = '下载完成';

}));

};

</script>

</html>

导出功能的体验没做好,差点被投诉了

把oss文件以blob的方式下载到当前页,然后再创建a标签

效果:

导出功能的体验没做好,差点被投诉了

使用

确定优化方案

最终选定了方案2。

原因是方案2改动最小,可以快速响应及时解决用户关心的问题。

思考

方案1:

优点:

  1. 可以由后端灵活自定义浏览器下载时的文件名。没有兼容性问题
  2. 代码实现简单。代码量少,实现简单

缺点:

  1. 服务器带宽打满后会影响其它功能的使用。服务器写数据到浏览器会占用服务器网卡的总带宽,如果打满,其它功能也用不了。可以把带宽想象成一座桥,大文件就像一个大卡车。
  2. 影响到服务器的稳定性。大文件生成及传输过程会持续占用服务器内存。服务器的内存是有限的,下载大文件的功能占用了,其它功能就不能正常工作了。
  3. 分布式环境中,增加了代码的复杂度。Feign或RestTempate在处理字节流时需要特殊的配置,在升级这些http客户组件时,也需要验证对这些已有功能的影响。

方案2:

继续阅读