Fetch 请求不转换BLOB正常显示GBK编码的数据

1,268 阅读3分钟

最近需要升级Google chrome的浏览器扩展。V3版本背景页不在支持XHR,请求需要使用Fetch。因为请求中使用大量的第三方api,有相当一部分api还是GBK的编码,请求回来的就是乱码,因为fetch只能是utf-8。

之前写deno爬虫遇到过这样的情况,当时直接就将fetch的请求结果转换成了blob,在使用FileReader回显结果。看是完美了,把它写到扩展中,然后运行,问题来了,控制台请求结果无了,一上来所有接口的请求结果全无了,但是打印没有问题,就是说,我除了看不见请求结果外,其他没有毛病。

// 使用blob来回显gbk数据
fetch(url, { method: 'get' })
    .then((res) => res.blob())
    .then((res) => {
      return new Promise((resolve) => {
        let reader = new FileReader()
        let resVal = null
        reader.readAsText(res, 'gbk')
        reader.onload = () => {
          resolve(reader.result)
        }
      })
    })

关键没有请求结果,我怎么调试,结果对错全靠猜吗?一开始还因为哪里写错了,导致接口请求出现问题,查了一个早上无果,最后发现接口应该是正常的,因为数据正常回来了,只是控制台看不到接口和响应信息。最后测试发现,就是转blob的问题。

image.png

image.png

解决这个属实太难了,查了半天无果,全网fetch转gbk都是blob。困惑了一天,难道就真的不行吗,不甘心啊。去蹲个厕所,突然想到,fetch对于(Response 流)的转换应该还有一个:ArrayBuffer,那么ArrayBuffer能不能转gbk嘞,使用ArrayBuffer会不会也导致控制台看到信息?

先确定一下,使用ArrayBuffer会不会也导致控制台看到信息?把上面的代码改改:

- .then((res) => res.blob())
+ .then((res) => res.arrayBuffer())

然后打印一下,能看到结果了,请求结果和响应都正常。就说明起码arrayBuffer是能用了,最关键的一点来了,arrayBuffer是否能正常显示出gbk的信息?

一番摸索,搞定,如果需要回显arrayBuffer的信息到字符串,可以调用TextDecoder的decode方法

先看看MDN对TextDecoder得定义:

TextDecoder 接口表示一个文本解码器,一个解码器只支持一种特定文本编码,例如 utf-8iso-8859-2koi8cp1261gbk 等等。解码器将字节流作为输入,并提供代码点流作为输出。

刚刚好TextDecoder支持编码, 在继续改造一下之前的代码:

.then(res => res.arrayBuffer())
.then(res => {
    return new TextDecoder('gbk').decode(res)
})

运行,这不就可以了嘛。不过还有一点点小问题,TextDecoder 得到的是字符串,但是结果返回的结果有些是json、有些是字符串(网页),那么如果区分?

.then(res => {
    // return new TextDecoder('gbk').decode(res)
    try {
        return JSON.parse(new TextDecoder('gbk').decode(res))
    } catch(err) {
        return new TextDecoder('gbk').decode(res)
    }
})

没错,简单粗暴。直接调用JSON.parse来转换TextDecoder的结果,如果符合json字符串,就可以被解得js对象,不行那就直接返回字符串了,完美收工,耗时一天半。