当导入导出为同一个接口时,会产生什么样的“化学反应”?
业务需求中出现一个接口既有导入功能,又需要有导出功能,接口具体情况如下:
导入excel表格接口中,前端传递上传的excel文档,后端判断文档是否格式正常,如果格式正常,则返回json格式的成功数据,如果格式不成功,则返回错误的excel文档,前端负责把错误的excel文档导出,第三种情况,如果5s内再次导入文档,则后端返回不能频繁导入的错误json数据。
总结一下:
情况1:前端导入excel表格,触发请求,后端响应文件,blob格式,导入数据失败,前端导出文件;
情况2:前端导入excel表格,触发请求,后端响应json格式,导入数据正常,存储到数据库中;
情况3:前端5s内再次导入excel表格,触发请求,后端响应json格式,导入数据失败,不能频繁导入;
情况1:导出excel由于格式问题打不开
导出接口处理如下:
const apiImportExcel = (params) => {return axiosBizcenterMarketingAct.request({url: 'https//www.xxx.com/importExcelOptLessNum',method: 'post',data: params})
}// 上传excel,失败下载excel,成功返回msg
const uploading = async (data) => {uploadLoading.value = truelet file = data.filelet params = new FormData()params.append('file', file)let res = await apiImportExcel(params)console.log(res, 1234)blobData.value = resisShowDownload.value = trueuploadMessage.value = '处理失败,请整理表格后重新上传文档'uploadLoading.value = falsedialogVisible.value = true
}// 导出
const downErrorExcel = () => {let fileName = '错误提示.xlsx'if (!blobData.value) {return}const blob = new Blob([blobData.value], { type: 'application/vnd.ms-excel' })const url = URL.createObjectURL(blob)downloadCommon(url, fileName)
}// 下载处理
const downloadCommon = (url: string, fileName: string) => {const link = document.createElement('a')link.style.display = 'none'link.href = urllink.setAttribute('download', fileName)document.body.appendChild(link)link.click()document.body.removeChild(link) // 下载完成移除元素
}
控制台输出为
axios拦截器输出为
解决问题1
由于后端返回文件时,前端必须设置responseType为blob,否则导出的excel会由于格式问题,打不开;
// 上传excel
const apiImportExcel = (params) => {return axiosBizcenterMarketingAct.request({url: 'https"//www.test.com/importExcelOptLessNum',method: 'post',responseType: 'blob',data: params})
}
情况2:导入成功,返回的内容为blob类型,json字符读取不到
如下图:控制台上方为拦截器输出内容,下方为拿到的data数据
但是当设置了响应格式为blob,则当后端返回json格式时,前端只能读到blob数据,blob数据看不到里面的内容。
相信大家看到了虽然返回都是blob格式,但是情况1和情况2返回的blob格式的type不一样。所以可以如下处理:
解决问题2:
处理代码如下:
// 上传excel,失败下载excel,成功返回msg
const uploading = async (data) => {uploadLoading.value = truelet file = data.filelet params = new FormData()params.append('file', file)let res = await apiImportExcel(params)console.log(res, 1234)if(res.type === 'text/xml') {isShowDownload.value = trueuploadMessage.value = '处理成功,成功导入'} else {blobData.value = resisShowDownload.value = trueuploadMessage.value = '处理失败,请整理表格后重新上传文档'}uploadLoading.value = falsedialogVisible.value = true
}
情况3:第三种情况和第二种情况怎么区分?
由于情况2和情况3返回都是json格式的问题,提示用户接口处理是否成功,所以正常的流程是后端接口返回的什么样的字符串,前端页面直接提示用户接口处理结果,而且目前是三种情况,将来扩展可能接口出现第四种第五种情况,所以使用上面的type判断,并不合适。
解决问题3:
不设置responseType为blob,会导致excel因为格式问题打不开,所以只能让接口返回blob格式,那就只能当接口返回类型为text/xml的时候,将blob解析成json格式,然后再读取json字符串了。
如何解析blob格式呢?下面有三种方法:
// 方法1const getJSONObjectFromBlob = (blob: Blob) => {const reader = new FileReader()reader.readAsArrayBuffer(blob)reader.onload = function () {console.info('reader.result>>>', reader.result) //ArrayBuffer {}//将 ArrayBuffer 转换成Blobconst buf = new Uint8Array(reader.result as ArrayBuffer)let enc = new TextDecoder('utf-8')let text = enc.decode(buf)try {const obj = JSON.parse(text)return obj} catch (error) {console.error('解析错误', error)return error}}}// 方法2const getJSONObjectFromBlob = (blob: Blob) => {const text = await (new Response(blob)).text();try {const obj = JSON.parse(text)return obj} catch (error) {console.error('解析错误', error)return error}}// 方法3const getJSONObjectFromBlob = (blob: Blob) => {const text = await blob.text();try {const obj = JSON.parse(text)return obj} catch (error) {console.error('解析错误', error)return error}}
看到这里,大家可能看到方法2和方法3都是读取Blob对象原型链上的text()方法进行解析,其实text()方法就是根据方法1这样设计的,不过目前这个方法1有点小瑕疵,大家能够发现吗?
方法1中使用FileReader对象去读取blob,然后通过onload去解析,不知道大家是否了解onload事件,onload是一个异步任务,所以js引擎走到这里不会去执行内部逻辑,而是等函数执行完才去宏任务队列中取出它,并执行。那函数还有未执行呢?走到这里,函数还没有返回值,所以大家应该猜到了吧,这里会返回undefined。
那怎么解决呢?大家有没有想到await和async关键字,但是使用这两个关键字,必须要是一个promise,所以把方法1改造成返回promise,然后去读取json对象,使用await关键字等待,promise响应再执行后续操作,就可以啦~~~
代码如下:
const getTextFromBlob = (blob: Blob): Promise<string> => {//将Blob 对象转换成 ArrayBufferreturn new Promise((resolve, reject) => {const reader = new FileReader()reader.readAsArrayBuffer(blob)reader.onload = function () {console.info('reader.result>>>', reader.result) //ArrayBuffer {}// 经常会遇到的异常 Uncaught RangeError: byte length of Int16Array should be a multiple of 2// const buf = new int16array(reader.result);// console.info(buf);//将 ArrayBuffer 转换成Blobconst buf = new Uint8Array(reader.result as ArrayBuffer)let enc = new TextDecoder('utf-8')resolve(enc.decode(buf))}reader.onerror = function (e) {console.error('转换成ArrayBeffer失败')reject(e)}})
}
// 解析 text/xml类型的blob, 成为JSON对象(失败则是json字符串)
export async function getJSONObjectFromBlob(blob: Blob): Promise<Record<string, unknown> | unknown> {let text = ''try {text = await getTextFromBlob(blob)const obj: Record<string, unknown> = JSON.parse(text)return obj} catch (error) {return error}
}
好了,为了能够通熟易懂,我还是使用了最简单的方法,代码如下:
const apiImportExcel = (params) => {return axiosBizcenterMarketingAct.request({url: 'https"//www.test.com/importExcelOptLessNum',method: 'post',data: params})
}
// 解析application/json类型的blob, 成为JSON对象(失败则是json字符串)const getJSONObjectFromBlob = (blob: Blob) => {const text = await blob.text();try {const obj = JSON.parse(text)return obj} catch (error) {console.error('解析错误', error)return error}}// 上传excel,失败下载excel,成功返回msgconst uploading = async (data) => {uploadLoading.value = truelet file = data.filelet params = new FormData()params.append('file', file)let res = await apiImportExcel(params)if (res.type === 'application/json') {// 读取BlobgetJSONObjectFromBlob(res).then((res) => {uploadMessage.value = res.msg})} else {blobData.value = resisShowDownload.value = trueuploadMessage.value = '处理失败,请整理表格后重新上传文档'}uploadLoading.value = falsedialogVisible.value = true}const downloadCommon = (url: string, fileName: string) => {const link = document.createElement('a')link.style.display = 'none'link.href = urllink.setAttribute('download', fileName)document.body.appendChild(link)link.click()document.body.removeChild(link) // 下载完成移除元素}
写到这里,有人会想到使用axios去请求接口会出现这种情况,那使用fetch呢?window对象自带的请求接口的方法,目前兼容性已经满足大部分浏览器了,所以我又做了以下内容
使用fetch请求
我们直接改造上面的uploading函数就行,说干就干
// 上传excel,失败下载excel,成功返回msgconst uploading = async (data) => {uploadLoading.value = truelet file = data.filelet params = new FormData()params.append('file', file)window.fetch("https//www.xxx.com/importExcelOptLessNum", {method: 'post',body: params,headers: {'Authorization': accessToken,}}).then(res => {console.log(res, '----1---')return res.json();// return res.blob();}).tnen(res => {console.log(res, '0----3---------')blobData.value = res;isShowDownload.value = trueuploadMessage.value = '处理失败,请整理表格后重新上传文档';}).catch(err => {console.error(err)});uploadLoading.value = falsedialogVisible.value = true}
结果就是不行,从上图中可以看到,fetch确实不需要在请求头这里设置响应体类型,但是当数据响应给客户端时,必须要调用Response中的json()方法或者blob()方法,但在这之前,无法判断res是什么数据类型。
总结一下:
虽然情况1和情况2两次的blob的类型不一致,一个是application/vnd.ms-excel类型,一个是text/xml,但是由于第三种情况的介入,必须要获取到后端返回的json格式数据显示。
设置了responseType为blob类型,接口返回的数据如下:
没有设置responseType时,接口返回的数据如下:
目前是 解析 text/xml类型的blob, 成为JSON对象。
更多关于Blob对象的知识,可以点击链接查看:https://developer.mozilla.org/zh-CN/docs/Web/API/Blob
如果大家有什么更好的处理方法,欢迎评论区讨论~~~
当导入导出为同一个接口时,会产生什么样的“化学反应”?相关推荐
- oracle 导出写入文件失败怎么办,编辑dmp文件解决导入导出问题(oracle 922错误 ,缺少或无效选项)...
导入导出的dmp文件时出现如下错误 经由常规路径导出由EXPORT:V09.02.00创建的文件 已经完成ZHS16GBK字符集和AL16UTF16 NCHAR 字符集中的导入 IMP-00017: ...
- 关于MSSQL导入导出时主键与约束丢失的问题解决
导入数据时,使用默认选项,会丢失主键.约束.默认值等属性,按如下步骤操作: -->导出向导 -->选择数据源 -->选择目的 -->指定表复制或查询:不要使用默认选项,选 ...
- SqlServer的SSIS导入导出数据时找不到连接错误处理
SqlServer的SSIS导入导出数据时找不到连接错误处理 报错: 找不到连接"SourceConnectionOLEDB".如果找不到特定的连接元素,Connections集合 ...
- oracle导入java包时出错,Oracle导入导出的常见错误
做数据的导入导出操作,最常用的就是imp/exp和impdp/expdp,经常会碰见各种错误,之前总结过一些,<解决导入过程中出现的ORA-02289错误>.<生产数据导入测试环境碰 ...
- ORACLE数据库在导入导出时序列不一致的问题
ORACLE数据库在导入导出时序列不一致的问题 在使用ORACLE数据库时,当给一个表设置自增字段时,我们经常会使用到序列+触发器来完成.但当你需要对数据库进行导入导出时,序列很容易出问题. 当你将数 ...
- abaqus生成adams柔性体_专栏 | HyperMesh_To_Abaqus接口——模型导入导出问题
作者介绍 TechmanLXS 硕士 十余年工程经验擅长Hypermesh建模,Hyperworks全平台分析软件,abaqus软件.整车级被动安全(ls-dyna.Radioss),零部件级(mol ...
- oracle 39083,oracle数据库导入导出时ORA-39083;ORA-00439错误解决思路
ORA-39083: Object type TABLE:TEST.TEST_SS_NAME failed to create with error: ORA-00439: feature not e ...
- 下载ug时java更新未完成_UG导入导出Dwg,Dxf文件提示:转换未完成,网上说的方法都试了一遍没用怎么办?...
这是因为导不出DWG/DXF文件的原因有多种,你找到的解决方案可能并不是你所在的问题,也有可能是你没有按解决方案中的做到位. 你可以按以下几个原因排查: 1.高版本NX软件,在导入DWG\DXF的时候 ...
- easyexcel导入时读不到数据_SpringBoot中EasyExcel实现Excel文件的导入导出
前言 在我们日常的开发过程中经常会使用Excel文件的形式来批量地上传下载系统数据,我们最常用的工具是Apache poi,但是如果数据到底上百万时,将会造成内存溢出的问题,那么我们怎么去实现百万数据 ...
- oracle 39083错误,Oracle数据库导入导出时,ORA-39083;ORA-00439错误解决思路
ORA-39083: Object type TABLE:"TEST"."TEST_SS_NAME" failed to create with error: ...
最新文章
- YAML开源库yaml-cpp简介及使用
- combo:机器学习模型合并工具库
- 医学图像-颅内出血(转载+整理)
- 使用SAP WebIDE创建开发Java应用,并且在浏览器里调试
- RISC-V工具链环境(基于Debian/Linux操作系统)
- java中datetime类型转换,Java中日期格式和其他类型转换详解
- VMware虚拟机下安装Ubuntu16.04镜像完整教程
- [No000014A]Linux简介与shell编程
- Android事件处理之处理键盘事件
- [System Design] 系统设计 (2) -- 数据库设计
- Zuul使用Ribbon配置自动重试
- 免费的html模板开源网站
- contiki CC2530 CC2430例子描述
- [python爬虫] Selenium爬取新浪微博内容及用户信息
- smtp匿名邮件发送小记
- Ubuntu 编译最新LLVM套件:LLVM 16.0
- 程序员夏天穿格子衫,那么冬天穿什么?答案扎心了
- Android开发仿微信支付宝的支付密码布局
- python 编码gbk_python 处理中文文件时的编码问题,尤其是utf-8和gbk
- [NFC]NFC 客户 Support 流程
热门文章
- Wiki引擎mediawiki
- JumpServer登录提示连接WebSocket失败
- 如何关掉visual studio 2012 实时调试器
- 程序化交易逆境中的奋斗史!
- 学习笔记之 初试Linux遇到的问题
- c语言kill暂停和恢复进程,Linux暂停和恢复进程
- 解决MySql保存数据java.sql.SQLException: Incorrect string value: ‘\xF0\x9F\x91\x87\xE5\x91...‘
- C 语言 —— 命名规则
- ⛽zipkin2.reporter.InMemoryReporterMetrics导致服务器CPU100%及应用OOM问题排查和解决
- Byond Compare软件