你不常用的 FileReader 能干什么?

在这里插入图片描述

前言

欢迎关注同名公众号《熊的猫》,文章会同步更新,也可快速加入前端交流群!

本文灵感源于上周小伙伴遇到一个问题:

一个本该返回 Blob 类型的下载接口,却返回了 JSon 类型的内容!!!

1C306E8E.jpg

这会有什么问题呢?

按原逻辑就是调用该接口后,就会一股脑把该接口接返回过来的内容,直接经过 Blob 对象 转换后再通过隐藏的 <a> 标签实现下载。

但是有一个问题,那就是接口也是需要进行各种逻辑处理、判断等等,然后再决定是给前端响应一个正常的 Blob 格式的文件流,还是返回相应 JSon 格式的异常信息 等等。

如果返回了 JSon 格式的异常信息,那前端应该给用户展示信息内容,而不是将其作为下载的内容!

1C3802FC.gif

FileReader 实现 Blob 从 String 到 JSON

复现问题

为了更直观看到对应的效果,我们这里来简单模拟一下前后端的交互过程吧!

前端

由于小伙伴发送请求时使用的是 Axios,并且设置了其对应的 responsetype:blob | arraybuffer,所以这里我们也使用 Axios 即可,具体如下:

    // 发起请求const request = () => {axios({method: 'get',url: 'http://127.0.0.1:3000',responseType: 'arraybuffer'}).then((res) => {// 转换为 bloc 对象const blob = new Blob([res.data])// 获取导出文件名,decodeURIComponent为中文解码方法const fileName = decodeURIComponent(res.headers["content-disposition"].split("filename=")[1])// 通过a标签进行下载let downloadElement = document.createElement('a');let href = window.URL.createObjectURL(blob);downloadElement.href = href;downloadElement.download = fileName;document.body.appendChild(downloadElement);downloadElement.click();document.body.removeChild(downloadElement);window.URL.revokeObjectURL(href);});}

后端

这里我们就简单通过 koa 来实现将一个表格文件响应给前端,具体如下:

    const xlsx = require("node-xlsx");const Koa = require("koa");const app = new Koa();const cors = require("koa2-cors");// 处理跨域app.use(cors({origin: "*", // 允许来自指定域名请求maxAge: 5, // 本次预检请求的有效期,单位为秒methods: ["GET", "POST"], // 所允许的 HTTP 请求方法credentials: true, // 是否允许发送 Cookie}));// 响应app.use(async (ctx) => {// 文件名字const filename = "人员信息";// 数据const data = [{ name: "赵", age: 16 },{ name: "钱", age: 20 },{ name: "孙", age: 17 },{ name: "李", age: 19 },{ name: "吴", age: 18 },];// 表格样式const oprions = {"!cols": [{ wch: 24 }, { wch: 20 }, { wch: 100 }, { wch: 20 }, { wch: 10 }],};// JSON -> Bufferconst buffer = JSONToBuffer(data, oprions);// 设置 content-typectx.set("Content-Type", "application/vnd.openxmlformats");// 设置文件名,中文必须用 encodeURIComponent 包裹,否则会报异常ctx.set("Content-Disposition","attachment; filename=" + encodeURIComponent(filename) + ".xlsx");// 文件必须设置该请求头,否则前端拿不到 Content-Disposition 响应头信息ctx.set("Access-Control-Expose-Headers", "Content-Disposition");// 将 buffer 返回给前端ctx.body = buffer;});// 将数据转成 Bufferconst JSONToBuffer = (data, options = {}) => {let xlsxObj = [{name: "sheet",data: [],},];data.forEach((item, idx) => {// 处理 excel 表头if (idx === 0) {xlsxObj[0].data.push(Object.keys(item));}// 处理其他 excel 数据xlsxObj[0].data.push(Object.values(item));});// 返回 buffer 对象return xlsx.build(xlsxObj, options);};// 启动服务app.listen(3000);

正常效果展示

1.gif

异常效果展示

可以看到当返回的内容为 JSON 格式 的内容时,原本逻辑在获取 filename 处就发生异常了,即使这一块没有发生异常,被正常下载下来也是不对的,因为这种情况应该要进行提示。

1.gif

并且此时直接去访问 res.data 得到的也不是一个 JSON 格式 的内容,而是一个 ArrayBuffer

在这里插入图片描述

返回的明明是 JSON ,但是拿到的却是 ArrayBuffer?

responseType 惹的祸

还记得我们在通过 Axios 去发起请求时设置的 responseType:'arraybuffer' 吗?

没错,就是因为这个配置的问题,它会把得到的结果给转成设置的类型,所以看起是一个 JSON 数据,但实际上拿到的是 Arraybuffer

这个 responseType 实际上就是 XMLHttpRequest.responseType,可点击该链接自行查看。

不设置 responseType 行不行?

那么既然是这个配置的问题,那么我们不设置不就好了!

确实可行,如下是未设置 responseType 获取到的结果:

在这里插入图片描述

但也不行,如果不设置 responseType 或者设置的类型不对,那么在 正常情况 下(即 文件被下载)时 会导致文件格式被损坏,无法正常打开,如下:

在这里插入图片描述

FileReader 来救场

实际上还有个比较直接的解决方案,那就是把接收到的 Arraybuffer 转成 JSON 格式不就行了吗?

1CB04D6B.jpg

没错,我们只需要通过 FileReader 来完成这一步即可,请看如下示例:

// json -> blob
const obj = { hello: "world" };const blob = new Blob([JSON.stringify(obj, null, 2)], {type: "application/json",
});console.log(blob) // Blob {size: 22, type: 'application/json'}// blob -> json
const reader = new FileReader()reader.onload = () => {console.log(JSON.parse(reader.result)) // { hello: "world" }
}reader.readAsText(blob,  'utf-8')

是不是很简单啊!

值得注意的是,并不是任何时候都需要转成 JSON 数据,就像并不是任何时候都要下载一样,我们需要判断什么时候该走下载逻辑,什么时候该走转换成 JSON 数据。

怎么判断当前是该下载?还是该转成 JSON?

这个还是比较简单的,换个说法就是判断当前返回的是不是文件流,下面列举较常见的两种方式。

根据 filename 判断

正常情况来讲,再返回文件流的同时会在 Content-Disposition 响应头中添加和 filename 相关的信息,换句话说,如果当前没有返回 filename 相关的内容,那么就可以将其当做异常情况,此时就应该走转 JSON 的逻辑。

不过需要注意,有时候后端返回的某些文件流并不会设置 filename 的值,此时虽然符合异常情况,但是实际上返回的是一个正常的文件流,因此不太推荐这种方式

208EA3E8.gif

根据 Content-Type 判断

这种方式更合理,毕竟后端无论是返回 文件流 或是 JSON 格式的内容,其响应头中对应的 Content-Type,必然不同,这里的判断更简单,我们直接判断其是不是 JSON 类型即可。

更改后的代码,如下:

axios({method: 'get',url: 'http://127.0.0.1:3000',responseType: 'arraybuffer'}).then(({headers, data}) => {console.log("FileReader 处理前:", data)const IsJson = headers['content-type'].indexOf('application/json') > -1;if(IsJson){const reader = new FileReader()// readAsText 只接收 blob 类型,因此这里需要先将 arraybuffer 变成 blob// 若后端直接返回的就是 blob 类型,则直接使用即可reader.readAsText(new Blob([data], {type: 'application/json'}), 'utf-8')reader.onload = () => {// 将字符内容转为 JSON 格式console.log("FileReader 处理后:", JSON.parse(reader.result))}return}// 下载逻辑download(data)});

值得注意的是,readAsText 只接收 blob 类型,因此这里需要先将 arraybuffer 变成 blob,若后端直接返回的就是 blob 类型,则直接使用即可。

在这里插入图片描述

FileReader 还能干什么?

以上是使用 FileReader 解决一个实际问题的例子,那么除此之外它还有什么应用场景呢?

不过我们还是先来了解一下 FileReader 的一些相关内容吧!!!

FileReader 是什么?

FileReader 对象允许 Web 应用程序 异步读取 存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 FileBlob 对象指定要读取的文件或数据。

不过还要注意如下两条规则:

  • FileReader 仅用于以安全的方式从用户(远程)系统读取文件内容,它不能用于从文件系统中按路径名简单地读取文件
  • 要在 JavaScript 中按路径名读取文件,应使用标准 Ajax 解决方案进行 服务器端文件读取

总结起来就是,FileReader 只能读取 FileBlob 类型的文件内容,并且不能直接按路径的方式读取文件,如果需要以路径方式读取,最好要通过 服务端 返回流的形式。

四种读取方式

FileReader 可以如下四种方式读取目标文件:

  • FileReader.readAsArrayBuffer()

    • 开始读取指定的 Blob中的内容,读取完成后,result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象
  • FileReader.readAsBinaryString()非标准

    • 开始读取指定的Blob中的内容,读取完成后,result 属性中将包含所读取文件的 原始二进制数据
  • FileReader.readAsDataURL()

    • 开始读取指定的Blob中的内容,读取完成后,result 属性中将包含一个 data: URL 格式的 Base64 字符串以表示所读取文件的内容
  • FileReader.readAsText()

    • 开始读取指定的Blob中的内容,读取完成后,result 属性中将包含一个 字符串 以表示所读取的文件内容

如上对应的方法命名十分符合顾名思义的特点,因此可以很容易看出来在不同场景下应该选择什么方法,并且如上方法一般都会配合 FileReader.onload 事件FileReader.result 属性 一起使用。

FileReader 的其他应用场景

预览本地文件

通常情况下,前端选择了相应的本地文件(图片、音/视频 等)后,需要通过接口发送到服务端,接着服务端在返回一个相应的预览地址,前端在实现支持预览的操作。

如果说现在有一个需要省略掉中间过程的需求,那么你就可以通过 FileReader.readAsDataURL() 方法来实现,但是要考虑文件大小带来转换时间快慢的问题。

这一部分比较简单,就不贴代码占篇幅了,效果如下:

1.gif

传输二进制格式数据

通常在上传文件时,前端直接将接收到的 File 对象以 FormData 发送给后端,但如果后端需要的是纯二进制的数据内容(即不需要再被构造)怎么办?

此时我们就可以使用 FileReader.readAsArrayBuffer() 来配合,为啥不用 FileReader.readAsBinaryString(),因为它是非标准的,而且 ArrayBuffer 也是原始的 二进制数据

具体代码如下:

// 文件变化
const fileChange = (e: any) => {const file = e.target.files[0]const reader = new FileReader()reader.readAsArrayBuffer(file)reader.onload = () => {upload(reader.result, 'http://xxx')}
}// 上传
const upload = (binary, url) => {var xhr = new XMLHttpRequest();xhr.open("POST", url);xhr.overrideMimeType("application/octet-stream");//直接发送二进制数据xhr.send(binary);// 监听变化xhr.onreadystatechange = function (e) {if (xhr.readyState === 4) {if (xhr.status === 200) {// 响应成功       }}}
}

最后

欢迎关注同名公众号《熊的猫》,文章会同步更新,也可快速加入前端交流群!

上面我们通过 FileReader 解决了一个实际问题,同时也简单介绍了其相应的使用场景,但这个场景具体是否是用于你的需求还要具体分析,不能盲目使用。

以上就是本文的全部内容了,希望本文对你有所帮助!!!

21E0754A.jpg

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

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

相关文章

HTML之表单设计

1、HTML表单 HTML表单是用于收集用户输入的信息&#xff0c;并将用户输入的内容信息传到后台服务器中。 表单是通过form标签实现。 特别注意&#xff1a;如果一些内容提交后&#xff0c;没有将内容提交给后台服务器&#xff0c;那么需要添加一个name属性&#xff0c;语法&am…

Stable Diffusion 3.5 震撼发布!最新开源 AI 图像生成模型,艺术创作必备神器!

❤️ 如果你也关注大模型与 AI 的发展现状&#xff0c;且对大模型应用开发非常感兴趣&#xff0c;我会快速跟你分享最新的感兴趣的 AI 应用和热点信息&#xff0c;也会不定期分享自己的想法和开源实例&#xff0c;欢迎关注我哦&#xff01; &#x1f966; 微信公众号&#xff…

【NOIP普及组】 装箱问题

【NOIP普及组】 装箱问题 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 有一个箱子容量为V&#xff08;正整数&#xff0c;0&#xff1c;&#xff1d;V&#xff1c;&#xff1d;20000&#xff09;&#xff0c;同时有n个物品&#xff08;0&…

KubeSphere 最佳实战:Kubernetes 部署集群模式 Nacos 实战指南

Nacos 是 Dynamic Naming and Configuration Service 的首字母简称&#xff0c;一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 是构建以服务为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。 在本文中&#xff0c;我将为您提供…

k8s备份恢复(velero)

velero简介 velero官网&#xff1a; https://velero.io/ velero-github&#xff1a; https://github.com/vmware-tanzu/velero velero的特性 备份可以按集群资源的子集&#xff0c;按命名空间、资源类型标签选择器进行过滤&#xff0c;从而为备份和恢复的内容提供高度的灵活…

怎么在线制作拼团活动

在这个快节奏的时代&#xff0c;我们总在寻找那份独特的购物乐趣与超值体验。传统购物模式已难以满足日益增长的个性化与性价比需求&#xff0c;而在线购物虽便捷&#xff0c;却常让人在琳琅满目的商品中迷失方向。正是在这样的背景下&#xff0c;一种全新的购物方式——“在线…

vue3处理货名的拼接

摘要&#xff1a; 货品的拼接规则是&#xff1a;【品牌】货名称/假如货品名称为空时&#xff0c;直接选择品牌为【品牌】赋值给货品&#xff0c;再选择品牌&#xff0c;会替换【品牌】&#xff1b;假如货名称为【品牌】名称&#xff0c;再选择品牌只会替换【品牌】&#xff0c;…

vue3项目页面实现echarts图表渐变色的动态配置

完整代码可点击vue3项目页面实现echarts图表渐变色的动态配置-星林社区 https://www.jl1mall.com/forum/PostDetail?postId202410151031000091552查看 一、背景 在开发可配置业务平台时&#xff0c;需要实现让用户对项目内echarts图表的动态配置&#xff0c;让用户脱离代码也…

2024下半年软考机考模拟系统已开放!小伙伴们速速练起来

千呼万唤使出来&#xff0c;软考机考的模拟练习系统已于10月23号正式开放&#xff01; 今年报名计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff08;软考&#xff09;的小伙伴们千万不要忘记哦&#xff01; 01、开放时间 据中国计算机技术职业资格网发…

基于AI识别数据的Vue.js图像框选标注

在数字化时代&#xff0c;图像识别技术的应用越来越广泛&#xff0c;尤其是在车牌识别、人脸识别等领域。本文将介绍如何使用Vue.js框架和JavaScript创建一个交互式组件&#xff0c;该组件不仅允许用户在图片上绘制多个区域&#xff0c;加载文字&#xff0c;还提供了清空功能。…

外包干了2个月,技术明显退步

回望过去&#xff0c;我是一名普通的本科生&#xff0c;于2019年通过校招有幸加入了南京某知名软件公司。那时的我&#xff0c;满怀着对未来的憧憬和热情&#xff0c;投入到了功能测试的岗位中。日复一日&#xff0c;年复一年&#xff0c;转眼间&#xff0c;我已经在这个岗位上…

常用shell指令

这些指令通常在adb shell环境中使用&#xff0c;或者通过其他方式&#xff08;如SSH&#xff09;直接在设备的shell中使用。 文件操作命令 ls&#xff1a;列出目录的内容 ls /sdcard cd&#xff1a;改变目录 cd /sdcard/Download pwd&#xff1a;打印当前工作目录 pwd cat&…

CV2通过一组轮廓点扣取图片

代码如下&#xff1a; import cv2 import numpy as np# 读取原始图像 original_image cv2.imread(img.png)# 定义一组轮廓点&#xff08;这里只是示例&#xff0c;你需要根据实际情况替换&#xff09; points np.array([[50, 100], [100, 200], [200, 150], [200, 50], [160…

负载均衡服务器攻击怎么解决最有效?

负载均衡服务器攻击怎么解决最有效&#xff1f;常见的有效解决方法包括&#xff1a;使用SYNCookie机制、限制ICMP包速率、基于源IP的连接速率限制、检测并丢弃异常IP包、配置访问控制列表&#xff08;ACL&#xff09;、设置虚拟服务器/服务器连接数量限制、设置HTTP并发请求限制…

【景观生态学实验】实验二 景观类型分类

实验目的 1.掌握ArcGIS软件的基本操作&#xff1a;通过课堂理论学习与实验课的实际动手操作&#xff0c;学习并熟练掌握如何利用ArcGIS软件对遥感影像进行一些较为基础的数据处理与分析工作&#xff0c;具体包括波段合成、图像镶嵌、图像裁剪与图像分类等&#xff1b; 2.熟悉…

基于STM32设计的养殖场环境监测系统(华为云IOT)

文章目录 一、前言1.1 项目介绍【1】项目开发背景【2】设计实现的功能【3】项目硬件模块组成【4】需求总结 1.2 设计思路1.3 系统功能总结1.4 开发工具的选择【1】设备端开发【2】上位机开发 二、部署华为云物联网平台2.1 物联网平台介绍2.2 开通物联网服务2.3 创建产品&#x…

微信小程序-获取头像和昵称

一.获取头像 1.将button组件open-type的值设置为chooseAvatar 2.通过bindchooseavatar事件回调获取到头像信息的临时路径 wxml文件代码&#xff1a; <view> <button class"btn" open-type"chooseAvatar" bindchooseavatar"chooseavatar&qu…

生成式人工智能

这个接龙的生成就是概率式的&#xff0c;下一个接龙的字是有概率的 本身就是在做文字接龙的游戏&#xff0c;不会搜索网上的资料

Zig语言通用代码生成器:逻辑,冒烟测试版发布

#1024程序员节 | 征文# Zig语言通用代码生成器&#xff1a;逻辑&#xff0c;冒烟测试版发布 Zig语言是一种新的系统编程语言&#xff0c;其生态位类同与C&#xff0c;是前一段时间大热的rust语言的竞品。它某种意义上的确非常像rust&#xff0c;尤其是在开发过程中无穷无尽抛错…

【哈工大_操作系统理论】L282930 生磁盘的使用从生磁盘到文件文件使用磁盘的实现

L4.3 生磁盘的使用 1、认识磁盘 选择磁道旋转扇区数据读写 哪一个柱面 C哪一个磁头 H哪一个扇区 S 2、第一层抽象&#xff1a;盘块号block 发送盘块号block&#xff0c;磁盘驱动根据 block 计算出 cyl、head、sec&#xff08;CHS&#xff09; 磁盘访问时间主要是寻道时间…