前端导出文件遇到了两种情况,一种是后端返回的文件流,一种是后端返回的是链接。直接使用都会导致乱码。
/**
* 格式化文件名称
*/
export function formatFilename(filename?: string, type?: string) {
if (filename) {
const allName = filename
?.split(';')
?.find((str: string) => str?.indexOf('~~filename~~ filename*=') !== -1)
?.replace('~~filename~~ filename*=', '');
const last = allName?.lastIndexOf('.') || 0;
const name = allName?.substring(0, last);
const suffix = allName?.substring(last, allName?.length);
return `${name}-${dayjs().unix()}${suffix}`;
}
return `${dayjs().unix()}.${type || 'csv'}`;
}
2023-05-29 做了更新,更新的原因在于:当文件名出现下划线时,下载出来的文件名后缀带了下划线,导致文件打不开。
导致的原因如下:
注:它俩的区别在于,filename 的 value 不进行编码,而flename遵从 RFC 59872]中定义的编码规则:由于filename是后来才定义的,许多老的浏览器并不支持,所以文档规定,当二者同时出现在头字段中时,需要采用filename*,忽略
filename。这里对fename*utt-8”%e2%82%ac%20rates做一下说明,这个写法一看可能会觉得很奇怪,它其实是用单引号作为分隔符,将等号右
边分成了三部分:第一部分是字符集(utf-8),中间部分是语言(未填写),最后的%e2%82%ac%20rates代表了实际值。
文件流
当后端返回的是二进制的文件流时,这时需要用Blob处理。
具体处理方法:
发起请求时,加上responseType: ‘blob’,让服务器知道我们需要的是Blob文件格式,这样处理过后返回的就不是乱码了
export function exportFile(file: ExportFileParams, type?: string) {
const types: any = {
pdf: 'application/pdf;',
};
if (file) {
/**
* 增加\ufeff UTF-8 BOM来标记文件
* @see https://stackoverflow.com/questions/17879198/adding-utf-8-bom-to-string-blob
*/
const blob = new Blob(['\ufeff', file?.data || file], {
//file?.data || file
type: `${type ? types[type] : 'text/plain;'}charset=UTF-8`,
});
// 通过 URLEncoder.encode(pFileName, StandardCharsets.UTF_8.name()) 加密编码的, 使用decodeURI(fileName) 解密
saveAs(blob, formatFilename(decodeURI(file?.filename || ''), type));
return true;
}
return false;
}
链接
当后端返回的是链接的情况下,这时我们设置responseType: ‘blob’,就没有用,依然是乱码,这是发现通过a标签可以直接下载。
解决方法:
export function exportBinarySystemFile(file: ExportFileParams) {
const link = document.createElement('a');
link.href = window.URL.createObjectURL(file.data);
link.download = formatFilename(decodeURI(file?.filename || ''));
link.click();
document.body.removeChild(link);
}