42 ajax 下载文件未配置 responseType blob 导致的文件异常

前言

这是一个最近的关于文件下载碰到的一个问题 

主要的情况是, 基于 xhr 发送请求, 获取下载的文件 

然后 之后 xhr 这边拿到 字节序列之后, 封装 blob 来进行下载 

然后 最开始我们这边没有配置 responseType 为 blob, arraybuffer, 然后 导致下载出来的 文件大小超过了一倍,m 并且解压出现了问题 

然后 增加了 responseType 配置为 blob 之后, 文件下载的功能就正常了 

这里来大致看一下 大体的一个情况, 因为 xhr 这边具体的 编码 response, responseText, responseXml 的代码查看不了, 因此看不到 字节序列 转换为 字符串的过程, 因此 这里的结论仅仅是一个 大致的推导

另外 js 这边 new Blob 的具体的代码 也是查看不了, 因此 查看不了 字符串 转换为 字节序列 的这个过程

 

 

测试用例

客户端这边发送请求的 demo 代码如下 

  ajax({method: 'post',url: '/xxx/file/batchDownload',data: data,// responseType: 'arraybuffer'}).then(res => {let blob = new Blob([res], {type: "application/zip"});const link = document.createElement("a");link.download = 'file.zip';link.style.display = "none";link.href = URL.createObjectURL(blob);document.body.appendChild(link);link.click();URL.revokeObjectURL(link.href);document.body.removeChild(link);}).finally(() => {this.setdownLoading(row, false)})

 

 

正常的情况

如下是一个列表展示, 原始正确的 zip 文件大小为 811667 字节 

// 原始文件大小 
blob 811667 // 原始字节序列 使用给定的编码编码为字符序列, 然后再使用相同的编码解码为字节序列 
new String(baos.toByteArray(), $charset).getBytes($charset)gbk : 810632 utf8 : 1475674 // 前端 xhr 代码中不配置 responseType 为 arraybuffer/blob 的情况 
xhr, without responseType blob, new Blob([res], {type: "application/zip"});
1476964

 

服务器这边响应的正常的 zip 文件大小如下, 是正确的, 可以正常打开 

f8e920720ead432db262d726bfd4443a.png

 

然后 我们看一下 正常的情况, 即 ajax 增加 responseType 为 blob/arraybuffer 的情况 

这里是 axios 中的基于 xhr 的一个适配器, 这里是具体的基于 XmlHttpRequest 发送请求的地方 

我们可以看到 response 为 blob, 然后 字节数为 811667 拿到的是正常的数据 

011045c7d5a246159862b19cb79be9cc.png

 

然后 进而再外层 业务 handle 处理的时候 也是拿到的正确的数据

然后 最终下载的压缩包, 正常 

631b159d4309404ab346a7a94d77a373.png

 

 

异常的情况

然后 这里可以看到的是 responseData 是一个字符串, 说明 他已经被转换过了 

因为 服务器那边传输的是原始的字节序列, 然后 这里被转换过了之后 可能会造成 字节序列 的数据丢失, 错误 

可以看到 这里数据大小是 1.5M 大概是原始数据的两倍, 字节转字符的编码可能是 gbk 或者 utf8

ec426fe35cc74bd4b0ee15ab0b4bfb8b.png

 

然后 业务这边再根据 字符串传唤为字节序列, 存放到 blob, 拿到的也是一个错误的数据 

可以看到这里数据量大小为 1476944 和上面表格统计的大小基本一致, 之所以说基本一致, 是因为多次下载 会有少许不同, 大小差距在 20字节左右 

c5e215cb58114cb2859ff525b5ba0d55.png

 

所以 综上问题就在于在这个 字节序列 转 字符序列 再转 字节序列 的过程中造成了数据的错误

这个过程 会经过两次字符编码体系的处理

  1. 如果这两次都是 相同的单字节编码, 那么不会出现问题
  2. 如果是两次都是 相同的多字节编码 则可能存在问题, 因为 目标字节序列 可能未必复合目标编码的格式约束, 然后 造成了数据的不可逆丢失
  3. 如果是两次是 不同的编码, 并且存在兼容的 codepoint, 而且 字节序列 中的数据均在这些兼容的 codepoint 范围内, 则不会出现问题, 否则 会出现数据错误

但是 目标字节序列, 是 zip 格式, 任何一个字节的错误 都可能造成整体文件 不符合 zip 的规范, 或者 最开始 验签的时候 就校验不通过

 

 

字节序列 使用 utf8编码转换为字符串, 然后再依据utf8编码转换为字节序列, 数据会不会丢失?

// 原始文件大小 
blob 811667 // 原始字节序列 使用给定的编码编码为字符序列, 然后再使用相同的编码解码为字节序列 
new String(baos.toByteArray(), $charset).getBytes($charset)gbk : 810632 utf8 : 1475674 

 

这里我们先来看下 这里例子中的文件的情况, 这里就解释了 不配置 responseType 为 arraybuffer/blob 的情况下下载出来的数据是错误的问题 

使用 utf8 的时候, 整个过程最终结果的字节序列长度为 1475674

9a21c8df869f4c10a866837678bc8694.png

 

使用 gbk 的时候, 整个过程最终结果的字节序列长度为 810632

ef3a7ab8f1da4e1aa0d2cf642a9157b0.png

 

然后 我们这里来看拿一下 上面的转换之后, 什么情况下 数据会丢失? 什么情况下 数据不丢失? 

如果 字节序列是满足 utf8 的编码规范, 则数据不会丢失, 否则 可能会有数据丢失

比如这里是原始字节序列 不满足 utf8 的编码规范, 然后 造成了数据的丢失, 原始 仅仅有 6 个字节, 转换之后却有 18 个字节, 并且 数据还存在错误

b68040b63ef2412eb288a1d544c27495.png

 

比如这里是原始字节序列 满足 utf8 的编码规范, 然后 可以看到的是 原始字节序列 和 目标字节序列 是相同的

222953164db24c58bf507f42be8baa43.png

 

 

但是我们的目标文件是一个 zip 格式的二进制文件, 我们不能确保它的字节序列满足 固定的字符编码[假设浏览器这边是以 utf8 进行编码解码] 

所以 如果是存在一个这个转换过程的话, 是可能存在 字节序列的数据的错误, 丢失 

进而 导致 下载下来的文件, zip 解压缩软件 识别出错

355f358219e7454197a1f77ab798d7d5.png

 

 

完 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/779494.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Image-Adaptive YOLO for Object Detection in Adverse Weather Conditions(IA-YOLO)

1、总体概述 基于深度学习的目标检测在常规条件的数据集可以获得不错的结果,但是在环境、场景、天气、照度、雾霾等自然条件的综合干扰下,深度学习模型的适应程度变低,检测结果也随之下降,因此研究在复杂气象条件下的目标检测方法…

警务数据仓库的实现

目录 一、SQL Server 2008 R2(一)SQL Server 的服务功能(二)SQL Server Management Studio(三)Microsoft Visual Studio 二、创建集成服务项目三、配置“旅馆_ETL”数据流任务四、配置“人员_ETL”数据流任…

C++原创2D我的世界1.00.3 QPBSv01优化版

上次更新遗漏了一些细节,这会我加上了 #include"bits/stdc.h" #include"Windows.h" #define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0) using namespace std; int a49,d49,w1,n0,m[100]{0},u[100]{0},t[100]{…

Vue 3中的`createApp`

Vue 3中的createApp 在Vue 3中,createApp被用来创建一个新的应用实例。它是Vue 3的入口点,用于启动应用。createApp函数接受一个对象参数,该对象包含多个属性,这些属性用于配置和扩展Vue应用。 基本用法 import {createApp } from vue; import App from ./App.vue;const…

k8s安装traefik作为ingress

一、先来介绍下Ingress Ingress 这个东西是 1.2 后才出现的,通过 Ingress 用户可以实现使用 nginx 等开源的反向代理负载均衡器实现对外暴露服务,以下详细说一下 Ingress,毕竟 traefik 用的就是 Ingress 使用 Ingress 时一般会有三个组件: …

【零基础C语言】预处理解析

预定义符号 c语言中设置一些预定的符号,我们可以直接使用 //列: __FILE__ //进⾏编译的源⽂件 __LINE__ //⽂件当前的⾏号 __DATE__ //⽂件被编译的⽇期 __TIME__ //⽂件被编译的时间 __STDC__ //如果编译器遵循ANSI C,其值为1,否…

基于SSM的高校普法系统(有报告)。Javaee项目。ssm项目。

演示视频: 基于SSM的高校普法系统(有报告)。Javaee项目。ssm项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结构,通过Spring Spri…

inkscape中文版本 G代码生成器(支持中英文及数字)使用

inkscape G代码生成器(支持中英文及数字)使用 1 inkscape安装1. 界面介绍2. 基本操作3. 图形编辑4. 图层管理5. 文件操作6. 高级功能7. 学习资源 2 laserengraver插件安装3 inkscape 使用candle 验证G代码效果 1 inkscape安装 跟着提示默认按键即可。 软…

HTML网站的概念

目录 前言: 1.什么是网页: 2.什么是网站: 示例: 3.服务器: 总结: 前言: HTML也称Hyper Text Markup Language,意思是超文本标记语言,同时HTML也是前端的基础&…

Git版本管理使用手册 - 5 - Git的.ignore文件语法

Git的.ignore文件 1.使用 .ignore文件可以忽略指定文件的版本控制。 2.语法: (1)#开头表示注释 (2)!开头表示不忽略匹配文件 (3)* 表示除/外,任何字符串 (4)?表示除/外,任何一个字符 (5)/ 如果模式的结尾有分割符/&am…

Linux 环境安装Nginx—源码和Dokcer两种安装方式

一、源代码编译安装Nginx 1.下载最新nginx源码 以nginx-1.25.3.tar.gz为例: 可以使用命令(联网):curl -O http://nginx.org/download/nginx-1.25.3.tar.gz或在官网下载.tar.gz 2.解压缩 tar -zxvf nginx-1.25.3.tar.gz cd nginx-1.25.3/ 3.安装依赖…

HarmonyOS实战开发-实现自定义弹窗

介绍 本篇Codelab基于ArkTS的声明式开发范式实现了三种不同的弹窗,第一种直接使用公共组件,后两种使用CustomDialogController实现自定义弹窗,效果如图所示 相关概念 AlertDialog:警告弹窗,可设置文本内容和响应回调…

【javaWeb 第八篇】后端-Mybatis(万字详细学习)

Mybatis框架 前言MybatisMybatis入门配置SQL提示JDBC数据库连接池lombok Mybatis基础操作日志输出Mybatis的动态SQL 前言 这篇是作者学习数据持久层框架Mybatis的学习笔记,希望对大家有所帮助,希望大家能够与作者交流讨论 Mybatis Mybatis是一款优秀的…

Android 开发 Spinner setSelection 不起作用

问题 Android 开发 Spinner setSelection 不起作用 详细问题 笔者进行Android项目开发,根据上一个页面用户选择数据,显示当前页面Spinner选项,调用 Spinner setSelection 不起作用。 相关java代码 spinner.setAdapter(adapter); …

uniapp对接萤石云 实现监控播放、云台控制、截图、录像、历史映像等功能

萤石云开发平台地址:文档概述 萤石开放平台API文档 (ys7.com) 萤石云监控播放 首先引入萤石云js js地址:GitHub - Ezviz-OpenBiz/EZUIKit-JavaScript-npm: 轻应用npm版本,降低接入难度,适配自定义UI,适配主流框架 vi…

C语言例4-35:鸡翁一,值钱五;鸡母一,值钱三;鸡雏三,值钱一。百钱买百鸡、问鸡翁、鸡母和鸡雏各几何?

方法一&#xff1a; 代码如下&#xff1a; //鸡翁一&#xff0c;值钱五&#xff1b;鸡母一&#xff0c;值钱三&#xff1b;鸡雏三&#xff0c;值钱一。百钱买百鸡、问鸡翁、鸡母和鸡雏各几何&#xff1f; //方法一&#xff1a; #include<stdio.h> int main(void) {int x…

简易挛生分拣系统设计

1 工效组合展示 2 方案规划设计 3 数字挛生建模 基础建模、动画设计、模型导出 4 软件体系架构 5 Web交互设计 5.1 页面架构 5.2 初始构造 5.3 模型运用 5.4 WS通信 5.5 运行展现 6 服务支撑编码 6.1 整体调度 6.2 WS服务 6.3 C/S通信 7 系统级调试完善

李雅普诺夫函数

李雅普诺夫函数是一种用于描述动力系统稳定性的数学工具。它在动力系统和控制理论中具有广泛的应用&#xff0c;尤其是在研究非线性系统的稳定性方面。 李雅普诺夫函数通常用于证明动力系统在一些条件下是稳定的。一个李雅普诺夫函数是一个实数值函数&#xff0c;通常表示为 V…

外文文献翻译以及阅读方法

翻译外文文献可以采取以下步骤&#xff1a; 阅读整篇文章&#xff0c;了解全文内容的大意和结构。 对文章进行逐段翻译&#xff0c;可以先用大意翻译整段&#xff0c;再细化到每个句子和词语。 在翻译过程中&#xff0c;要注意保持原文的逻辑结构和表达方式&#xff0c;尽量…

使用mybatis的@Interceptor实现拦截sql

一 mybatis的拦截器 1.1 拦截器介绍 拦截器是一种基于 AOP&#xff08;面向切面编程&#xff09;的技术&#xff0c;它可以在目标对象的方法执行前后插入自定义的逻辑。 1.2 语法介绍 1.注解Intercepts Intercepts({Signature(type StatementHandler.class, method “…