跳到主要内容

记Chrome浏览器BUG导致csv上传失败问题排查

· 阅读需 5 分钟

最近有业务反馈有个系统上传csv总是失败,提示“文件只能为csv格式,请重新上传”:

上传失败截图

但是,我在自己电脑上测试了多次都能正常上传。心想,这难道就是传说中的,不是代码的问题,而是用户电脑环境的问题吗? 于是,我与该业务会议沟通,通过查浏览器开发者工具中的网络记录,发现业务的电脑在上传时并没有发送上传请求就被拦截上传失败了。很显然,这应该是前端的问题。前端使用了Vue+elementUI,代码逻辑大概如下:

<template>
<el-upload
action="https://baidu.com"
:show-file-list="false"
:before-upload="beforeUpload"
>
<el-icon class="uploader-icon"><Plus /></el-icon>
</el-upload>
</template>

<script lang="javascript" setup>
import { ElMessage } from 'element-plus'

const beforeUpload = (file) => {
if (file.type !== 'text/csv') {
ElMessage.error('文件只能为csv格式,请重新上传')
return false
}
return true
}
</script>

使用elementUI提供的el-upload,在:before-upload中对file.type的值进行判断,如果不是text/csv就报错。

乍一看,这样似乎没什么问题,但是在该业务的电脑上测,file.type的值不是text/csv,而是application/vnd.ms-excel,而这个格式应该是2003版Excel的格式,而不是csv。

Windows系统通过后缀识别文件类型,而不是魔数,后缀与文件类型的映射存储在系统的注册表。由于我和前端的电脑系统都是MacOS,电脑性能孱弱所以没装Windows虚拟机,为了研究这个问题,我们联系使用Windows系统的QA,然后打开注册表编辑器查看.csv对应的文件类型,发现是application/vnd.ms-excel,但是QA的电脑是可以正常上传的,上传csv时file.type的值是text/csv

Windows系统在安装Office后,会写入注册表将.csv的文件类型设置为application/vnd.ms-excel。该业务和QA的电脑上都安装了Office,但上传时file.type的结果却有差别,并且该业务的这个问题属于特例,并没有很多人反映无法上传csv的问题,但我们又不好一直占用业务的电脑进行测试,只好自己研究。在经过我一顿搜索后,终于发现了问题的根源。

真相只有一个

其实这是Chrome浏览器曾经的一个BUG:incorrect mime-type for csv-files [40247558] - Chromium。 Chrome浏览器依次查找kPrimaryMappings、系统配置、kSecondaryMappings来确定文件后缀与文件类型的映射关系。Office会写入注册表,将.csv的文件类型设置为application/vnd.ms-excel,所以会出现csv文件的file.typeapplication/vnd.ms-excel而不是text/csv的问题。 为了解决这个问题,Chrome开发者就把.csvtext/csv的映射关系写到了kPrimaryMappings中,以下是源码中的描述:

// This is a primary mapping (overrides the platform) rather than secondary
// to work around an issue when Excel is installed on Windows. Excel
// registers csv as application/vnd.ms-excel instead of text/csv from RFC
// 4180. See https://crbug.com/139105.
{"text/csv", "csv"},

源码位置:chromium/net/base/mime_util.cc at b4ce9e07f6f5bde7c0020d5f36e5d0ad4f95e7da · chromium/chromium

这样,即使.csv被Office错误的映射为application/vnd.ms-excel,由于kPrimaryMappings的优先级更高,会优先读取kPrimaryMappings中配置的映射关系,.csv仍然映射为text/csv类型。

补充下Windows注册表如何配置文件后缀与文件类型的关联,以.csv为例:读取HKEY_CLASSES_ROOT\.csv\Content Type,不过这个位置是只读的,如果要写入,则是HKEY_LOCAL_MACHINE\Software\Classes\.csv\Content TypeHKEY_CURRENT_USER\Software\Classes\.csv\Content Type,并且后者的优先级更高。

后记:让业务安装最新版Chrome浏览器,问题解决。