当前位置: 首页 > article >正文

前端兼容处理接口返回的文件流或json数据

参考文档:JavaScript | MDN

参考链接:Blob格式转json格式,拿到后端返回的json数据_blob转json-CSDN博客

参考链接:https://juejin.cn/post/7117939029567340557

场景:导入上传文件,导入成功,返回json数据显示列表,导入失败后端返回二进制文件流。

一、代码实现

1、接口请求

/*** @description 导入*/
export const postCustGroupImport = (params?: any) => {return request(`${prefixPath}/custGroupManages/custGroupListImport`, {method: 'POST',data: params,responseType: 'blob',// headers: {//   'Content-Type': 'multipart/form-data',// },});
};

2、操作步骤

(1)点击要上传的文件

(2)上传文件

(3)导入正确的文件

请求参数:file为二进制文件流

返回格式

打印结果:

(4)导入失败的文件,返回结果

(5)代码:

    const handleUpload = async () => {// 请求接口中去除,直接用下面的// headers: {//   'Content-Type': 'multipart/form-data',// },// 设置'multipart/form-data'const formData = new FormData();formData.append('channelCode', channelCode);formData.append('file', fileList[0]);setUploading(true);// 1、导入文件上传const res = await postCustGroupImport(formData);console.log('res', res);setUploading(false);if (res.failed) {message.error(res.message, undefined, undefined, 'top');return;}/** 检查返回的 Blob 是否是 JSON 格式 */if (res.type === 'application/json') {const text = await res.text(); // 将 Blob 转换为文本const json = JSON.parse(text); // 将文本解析为 JSONconsole.log('JSON response:', json);// 上传成功if (Array.isArray(json) && json?.length > 0) {setStatus('success');message.success('上传成功', undefined, undefined, 'top');tableDs.loadData(json);return;}} else {// 上传失败console.error('返回的 Blob 类型不是 JSON:(错误文件)', res.type);setStatus('error');setErrorFile(res); // 把错误文件存储到本地,手动点击下载}};/** 下载错误文件 */const handleDown = () => {// 确保 errorFile 是一个有效的 Blob 对象if (!(errorFile instanceof Blob)) {console.error('errorFile 不是一个有效的 Blob 对象');return;}// 创建 Blob 对象const blob = new Blob([errorFile], {type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',});// 创建一个可访问的 URLconst downloadUrl = URL.createObjectURL(blob);// 使用 window.open 触发下载window.open(downloadUrl, '_blank');// 释放资源URL.revokeObjectURL(downloadUrl);};

三、完整代码

1、引用模块代码

2、导入模块代码

import { Button, Icon, message, Modal, Table } from 'choerodon-ui/pro';
import DataSet from 'choerodon-ui/dataset/data-set/DataSet';
import React, { useEffect, useMemo, useState } from 'react';
import formatterCollections from 'hzero-front/lib/utils/intl/formatterCollections';
import { ColumnProps } from 'choerodon-ui/pro/lib/table/Column';
import {commonModelPrompt,languageConfig,prmPrTemCode,TABLE_COLUMN_NUM,
} from '@/language/language';
import { CoverModelChooseProps } from '@/interface/customerBase/main';
import { handleSerialNum } from '@/utils/utils';
import { ColumnAlign } from 'choerodon-ui/dataset/enum';
import { ButtonColor, FuncType } from 'choerodon-ui/pro/lib/button/enum';
import { Upload } from 'choerodon-ui';
import { postCustGroupImport } from '@/api/customerBase/main';
import { importTableList } from './store';
import {SelectionMode,TableAutoHeightType,
} from 'choerodon-ui/pro/lib/table/enum';
import { Title } from '@ino/ltc-component-paas';
import moment from 'moment';
import { ErrorMessage, handleTotal } from '../../../hook';const Index: React.FC<CoverModelChooseProps> = ({/** 控制弹框显示/隐藏 */visible,/** 设置弹框显示/隐藏的回调函数 */setVisible,/** 弹框关闭后回调函数 */onSelect,/** 渠道编码 */channelCode = '',infoData,
}) => {const { chooseList = [] } = infoData;/** ds */const tableDs = useMemo(() => new DataSet(importTableList(chooseList)), []);const columns: ColumnProps[] = useMemo(() => {return [{header: TABLE_COLUMN_NUM,width: 60,align: ColumnAlign.left,renderer: ({ record }) => {return handleSerialNum(record);},},{ name: 'custCode' },{ name: 'custName' },{ name: 'productAuthCategoryId' },{ name: 'categoryCapacity' },{ name: 'custManager' },{name: 'disableMessage',renderer: ({ value, record }) => {const { custCode, productAuthCategoryId } = record?.toData();const arr = chooseList.filter((item: any) =>item.custCode === custCode &&productAuthCategoryId === item.productAuthCategoryId,);/** 列表中添加过的数据,手动设置文案:'列表中已添加' */return (<div style={{ color: 'red' }}>{arr.length === 0? value: languageConfig('customerBase.relevantInfo.tip.hasDisableMessage','列表中已添加该条数据。',)}</div>);},},];}, []);useEffect(() => {if (visible) {openModal();}}, [visible]);/** 弹框打开 */const openModal = () => {Modal.open({title: languageConfig('btn.add.importReleaseCustToList','导入关联客户列表',),style: { width: '70vw' },closable: true,maskClosable: false,keyboardClosable: false,onClose: () => {setVisible(false);},children: <Box />,onOk: async () => {if (tableDs?.selected.length === 0) {message.error(languageConfig('customerBase.relevantInfo.tip.pleaseChooseOne','请至少选择一条数据',),undefined,undefined,'top',);return false;}/** 1、导入的数据:处理 */const choose = tableDs.selected?.map((item: Record<any, any>) => {return {...item.toData(),joinDate: moment().format('YYYY-MM-DD HH:mm:ss'), // 入团时间(默认'当前')status: 'TO_BE_ACTIVE', // 状态(默认'待生效')};});/** 2、'已选数据'中存在的提示 */const matchingItems = choose.filter(itemChoose =>chooseList.some(itemChooseList => itemChooseList.custCode === itemChoose.custCode,),);if (matchingItems.length > 0) {message.error(languageConfig('customerBase.coverModel.tip.alreadySelected','已选数据中存在重复数据',),undefined,undefined,'top',);return false;}/** 3、总价超5k 校验 */const list = chooseList.concat(choose);if (handleTotal(list, 'categoryCapacity') > 5000) {ErrorMessage(languageConfig('customerBase.relevantInfo.tips.categoryCapacityPass','客户团总容量已超上限5000万,不可提交!',),);return false;}onSelect(choose);},});};/** 内容 */const Box = () => {const [fileList, setFileList] = useState<any>([]); // 文件列表const [uploading, setUploading] = useState(false); // 是否正在上传const [status, setStatus] = useState<any>(''); // 导入状态const [errorFile, setErrorFile] = useState<any>(''); // 导入失败,错误文件存储/** 上传文件 */const handleUpload = async () => {// 请求接口中去除,直接用下面的// headers: {//   'Content-Type': 'multipart/form-data',// },// 设置'multipart/form-data'const formData = new FormData();formData.append('channelCode', channelCode);formData.append('file', fileList[0]);setUploading(true);// 1、导入文件上传const res = await postCustGroupImport(formData);console.log('res', res);setUploading(false);if (res.failed) {message.error(res.message, undefined, undefined, 'top');return;}/** 检查返回的 Blob 是否是 JSON 格式,是json格式为'上传成功',否则为'上传失败' */if (res.type === 'application/json') {const text = await res.text(); // 将 Blob 转换为文本const json = JSON.parse(text); // 将文本解析为 JSONconsole.log('JSON response:', json);// 上传成功if (Array.isArray(json) && json?.length > 0) {setStatus('success');message.success('上传成功', undefined, undefined, 'top');tableDs.loadData(json);return;}} else {// 上传失败console.error('返回的 Blob 类型不是 JSON:(错误文件)', res.type);setStatus('error');setErrorFile(res);}};/** 下载错误文件 */const handleDown = () => {// 确保 errorFile 是一个有效的 Blob 对象if (!(errorFile instanceof Blob)) {console.error('errorFile 不是一个有效的 Blob 对象');return;}// 创建 Blob 对象const blob = new Blob([errorFile], {type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',});// 创建一个可访问的 URLconst downloadUrl = URL.createObjectURL(blob);// 使用 window.open 触发下载window.open(downloadUrl, '_blank');// 释放资源URL.revokeObjectURL(downloadUrl);};const handleRemove = file => {const index = fileList.indexOf(file);const newFileList = [...fileList];newFileList.splice(index, 1);setFileList(newFileList);};const beforeUpload = file => {setFileList([...fileList, file]);return false; // 返回 false 阻止自动上传};return (<>{/* 导入文件 */}<div style={{ display: 'flex' }}><Upload beforeUpload={beforeUpload} onRemove={handleRemove}><Button><Icon type="file_upload" />{languageConfig('customerBase.btn.chooseTheFileToImport','选择要导入的文件',)}</Button></Upload><ButtonfuncType={FuncType.raised}color={ButtonColor.primary}onClick={handleUpload}disabled={fileList.length === 0}loading={uploading}style={{ marginLeft: '12px' }}>{uploading? languageConfig('customerBase.label.Importing', '导入中'): languageConfig('customerBase.label.startImport', '开始导入')}</Button></div>{/* 导入成功的数据 */}{status === 'success' && (<><div style={{ marginTop: '12px' }}><Titletitle={languageConfig('customerBase.title.importSuccessData','导入成功的数据',)}/><TabledataSet={tableDs}columns={columns}pagination={false}alwaysShowRowBoxselectionMode={SelectionMode.click}selectedHighLightRow// autoHeight={{ type: TableAutoHeightType.maxHeight, diff: 100 }}renderEmpty={() => {return <div>暂无数据</div>;}}/></div></>)}{/* 导入失败 */}{status === 'error' && (<><div style={{ marginTop: '12px' }}><Titletitle={languageConfig('customerBase.title.importFailedData','导入失败的数据',)}/></div><div><a onClick={handleDown}><Icon type="file_download_black-o" />{languageConfig('customerBase.download.errorFile','下载错误文件',)}</a></div></>)}</>);};return <></>;
};export default formatterCollections({code: [prmPrTemCode, commonModelPrompt],
})(Index);

相关文章:

前端兼容处理接口返回的文件流或json数据

参考文档&#xff1a;JavaScript | MDN 参考链接&#xff1a;Blob格式转json格式&#xff0c;拿到后端返回的json数据_blob转json-CSDN博客 参考链接&#xff1a;https://juejin.cn/post/7117939029567340557 场景&#xff1a;导入上传文件&#xff0c;导入成功&#xff0c;…...

Eclipse 透视图 (Perspective)

Eclipse 透视图 (Perspective) Eclipse 是一款强大的集成开发环境(IDE),广泛应用于 Java 开发领域。其中,透视图(Perspective)是 Eclipse 中的一个核心概念,它将不同的工具和视图组合在一起,以便开发者能够更高效地完成特定的开发任务。本文将详细介绍 Eclipse 透视图…...

嵌入式硬件篇---滤波器

文章目录 前言一、模拟电子技术中的滤波器1. 基本概念功能实现方式 2. 分类按频率响应低通滤波器高通滤波器带通滤波器带阻滤波器 按实现方式无源滤波器有源滤波器 3. 设计方法巴特沃斯滤波器&#xff08;Butterworth&#xff09;切比雪夫滤波器&#xff08;Chebyshev&#xff…...

从零到一学习c++(基础篇--筑基期十一-类)

从零到一学习C&#xff08;基础篇&#xff09; 作者&#xff1a;羡鱼肘子 温馨提示1&#xff1a;本篇是记录我的学习经历&#xff0c;会有不少片面的认知&#xff0c;万分期待您的指正。 温馨提示2&#xff1a;本篇会尽量用更加通俗的语言介绍c的基础&#xff0c;用通俗的语言去…...

Java基础常见的面试题(易错!!)

面试题一&#xff1a;为什么 Java 不支持多继承 Java 不支持多继承主要是为避免 “菱形继承问题”&#xff08;又称 “钻石问题”&#xff09;&#xff0c;即一个子类从多个父类继承到同名方法或属性时&#xff0c;编译器无法确定该调用哪个父类的成员。同时&#xff0c;多继承…...

DPVS-2:单臂负载均衡测试

上一篇编译安装了DPVS&#xff0c;这一篇开启DPVS的负载均衡测试 &#xff1a; 单臂 FULL NAT模式 拓扑-单臂 单臂模式 DPVS 单独物理机 CLINET&#xff0c;和两个RS都是另一个物理机的虚拟机&#xff0c;它们网卡都绑定在一个桥上br0 &#xff0c; 二层互通。 启动DPVS …...

C#中提供的多种集合类以及适用场景

在 C# 中&#xff0c;有多种集合类可供使用&#xff0c;它们分别适用于不同的场景,部分代码示例提供了LeetCode相关的代码应用。 1. 数组&#xff08;Array&#xff09; 特点 固定大小&#xff1a;在创建数组时需要指定其长度&#xff0c;之后无法动态改变。连续存储&#xf…...

【蓝桥杯集训·每日一题2025】 AcWing 6135. 奶牛体检 python

6135. 奶牛体检 Week 1 2月21日 农夫约翰的 N N N 头奶牛站成一行&#xff0c;奶牛 1 1 1 在队伍的最前面&#xff0c;奶牛 N N N 在队伍的最后面。 农夫约翰的奶牛也有许多不同的品种。 他用从 1 1 1 到 N N N 的整数来表示每一品种。 队伍从前到后第 i i i 头奶牛的…...

【为什么用pg数据库用 != null 过滤不出null值】

为什么用pg数据库用 ! null 过滤不出null值 1. NULL 的特殊性质2. 为什么 ! null 无效3. 正确的过滤 NULL 的方式示例 4. 为什么 IS NULL 和 IS NOT NULL 有效5. 示例对比6. 总结 在 PostgreSQL 中&#xff0c;使用 ! null 过滤不出 NULL 值的原因与 SQL 标准中 NULL 的特殊性质…...

Classic Control Theory | 12 Real Poles or Zeros (第12课笔记-中文版)

笔记链接&#xff1a;https://m.tb.cn/h.Tt876SW?tkQaITejKxnFLhttps://m.tb.cn/h.Tt876SW?tkQaITejKxnFL...

Kubernetes开发环境minikube | 开发部署MySQL单节点应用

minikube是一个主要用于开发与测试Kubernetes应用的运行环境 本文主要描述在minikube运行环境中部署MySQL单节点应用 minikube start --force kubectl get nodes 如上所示&#xff0c;启动minikube单节点运行环境 minikube ssh docker pull 如上所示&#xff0c;从MySQL官…...

大厂数据仓库数仓建模面试题及参考答案

目录 什么是数据仓库,和数据库有什么区别? 数据仓库的基本原理是什么? 数据仓库架构是怎样的? 数据仓库分层(层级划分),每层做什么?分层的好处是什么?数据分层是根据什么?数仓分层的原则与思路是什么? 数仓建模常用模型有哪些?区别、优缺点是什么?星型模型和雪…...

腾讯SQL面试题解析:如何找出连续5天涨幅超过5%的股票

腾讯SQL面试题解析:如何找出连续5天涨幅超过5%的股票 作者:某七年数据开发工程师 | 2025年02月23日 关键词:SQL窗口函数、连续问题、股票分析、腾讯面试题 一、问题背景与难点拆解 在股票量化分析场景中,"连续N天满足条件"是高频面试题类型。本题要求在单表stoc…...

安装可视化jar包部署平台JarManage

一、下载 下载地址&#xff1a;JarManage 发行版 - Gitee.com &#x1f692; 下载 最新发行版 下载zip的里面linux和windows版本都有 二、运行 上传到服务器&#xff0c;解压进入目录 &#x1f69a; 执行java -jar jarmanage-depoly.jar 命令运行 java -jar jarmanage-dep…...

基于数据可视化+SpringBoot+安卓端的数字化OA公司管理平台设计和实现

博主介绍&#xff1a;硕士研究生&#xff0c;专注于信息化技术领域开发与管理&#xff0c;会使用java、标准c/c等开发语言&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架…...

输入搜索、分组展示选项、下拉选取,全局跳转页,el-select 实现 —— 后端数据处理代码,抛砖引玉展思路

详细前端代码写于上一篇&#xff1a;输入搜索、分组展示选项、下拉选取&#xff0c;el-select 实现&#xff1a;即输入关键字检索&#xff0c;返回分组选项&#xff0c;选取跳转到相应内容页 —— VUE项目-全局模糊检索 【效果图】&#xff1a;分组展示选项 >【去界面操作体…...

性能巅峰对决:Rust vs C++ —— 速度、安全与权衡的艺术

??关注&#xff0c;带你探索Java的奥秘&#xff01;?? ??超萌技术攻略&#xff0c;轻松晋级编程高手&#xff01;?? ??技术宝库已备好&#xff0c;就等你来挖掘&#xff01;?? ??订阅&#xff0c;智趣学习不孤单&#xff01;?? ??即刻启航&#xff0c;编…...

unity学习53:UI的子容器:面板panel

目录 1 UI的最底层容器&#xff1a;canvas 1.1 UI的最底层容器&#xff1a;canvas 1.2 UI的合理结构 2 UI的子容器&#xff1a;面板panel 2.1 创建panel 2.2 面板的本质&#xff1a; image &#xff0c;就是一个透明的图片&#xff0c;1个空容器 3 面板的属性 4 面板的…...

4-知识图谱的抽取与构建-4_2实体识别与分类

&#x1f31f; 知识图谱的实体识别与分类&#x1f525; &#x1f50d; 什么是实体识别与分类&#xff1f; 实体识别&#xff08;Entity Recognition&#xff09;是从文本中提取出具体的事物&#xff0c;如人名、地名、组织名等。分类&#xff08;Entity Classification&#x…...

elasticsearch在windows上的配置

写在最前面&#xff1a; 上资源 第一步 解压&#xff1a; 第二步 配置两个环境变量 第三步 如果是其他资源需要将标蓝的文件中的内容加一句 xpack.security.enabled: false 不同版本的yaml文件可能配置不同&#xff0c;末尾加这个 xpack.security.enabled: true打开bin目…...

详解分布式ID实践

引言 分布式ID&#xff0c;所谓的分布式ID&#xff0c;就是针对整个系统而言&#xff0c;任何时刻获取一个ID&#xff0c;无论系统处于何种情况&#xff0c;该值不会与之前产生的值重复&#xff0c;之后获取分布式ID时&#xff0c;也不会再获取到与其相同的值&#xff0c;它是…...

如何在 Vue 项目中为 `el-pagination` 设置中文

文章目录 前言1. 安装 Element Plus2. 引入中文语言包3. 配置中文语言环境4. 使用 el-pagination 组件5. 确保其他组件支持中文6. 语言切换&#xff08;可选&#xff09;总结 前言 在 Vue 项目中&#xff0c;Element Plus 是一个流行的 UI 组件库&#xff0c;它提供了许多常用…...

PostgreSQL:更新字段慢

目录标题 PostgreSQL 慢查询优化与 pg_stat_statements 使用1. 启用慢查询日志2. 使用 pg_stat_statements 扩展收集查询统计信息3. 查找执行时间较长的查询4. 分析慢查询的执行计划5. 优化查询6. 检查并发连接和系统资源7. 进一步优化8. 查看某条SQL1. **如何生成 query_id**2…...

【Rust中级教程】2.8. API设计原则之灵活性(flexible) Pt.4:显式析构函数的问题及3种解决方案

喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文&#xff09;&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 说句题外话&#xff0c;这篇文章一共5721个字&#xff0c;是我截至目前写的最长的一篇文章&a…...

【复习】Redis

数据结构 Redis常见的数据结构 String&#xff1a;缓存对象Hash&#xff1a;缓存对象、购物车List&#xff1a;消息队列Set&#xff1a;点赞、共同关注ZSet&#xff1a;排序 Zset底层&#xff1f; Zset底层的数据结构是由压缩链表或跳表实现的 如果有序集合的元素 < 12…...

STM32使用NRF2401进行数据传送

NRF2401是一款由Nordic Semiconductor公司生产的单片射频收发芯片&#xff0c;以下是关于它的详细介绍&#xff1a; 一、主要特点 工作频段&#xff1a;NRF2401工作于2.4~2.5GHz的ISM&#xff08;工业、科学和医疗&#xff09;频段&#xff0c;该频段无需申请即可使用&#xf…...

Fetch API 与 XMLHttpRequest:深入剖析异步请求的利器

Hi&#xff0c;我是布兰妮甜 &#xff01;在现代 Web 开发中&#xff0c;异步通信是实现动态和交互式用户体验的基石。XMLHttpRequest (XHR) 作为老牌劲旅&#xff0c;曾一度统治着这一领域。然而&#xff0c;随着 Fetch API 的横空出世&#xff0c;开发者们迎来了一个更现代、…...

如何生成traceid以及可视化展示

根据你的需求&#xff0c;以下是一些可以生成唯一 traceId 并用于分布式链路追踪的工具和项目&#xff0c;这些项目支持生成唯一的 traceId&#xff0c;并将其用于日志记录和分布式追踪&#xff1a; 1. OpenTelemetry OpenTelemetry 是一个开源的观测框架&#xff0c;支持生成…...

【LeetCode541】反转字符串

题目描述 给定一个字符串 s 和一个整数 k&#xff0c;从字符串开头算起&#xff0c;每计数至 2k 个字符&#xff0c;就反转这 2k 字符中的前 k 个字符。 如果剩余字符少于 k 个&#xff0c;则将剩余字符全部反转。 如果剩余字符小于 2k 但大于或等于 k 个&#xff0c;则反转前…...

DeepSeek、微信、硅基流动、纳米搜索、秘塔搜索……十种不同方法实现DeepSeek使用自由

为了让大家实现 DeepSeek 使用自由&#xff0c;今天分享 10 个畅用 DeepSeek 的平台。 一、官方满血版&#xff1a;DeepSeek官网与APP 首推&#xff0c;肯定是 DeepSeek 的官网和 APP&#xff0c;可以使用满血版 R1 和 V3 模型&#xff0c;以及联网功能。 网址&#xff1a; htt…...