如何将JavaScript对象转换为UTF-8 Blob以便下载?

12

我一直在尝试找到一个解决方案,但是没有找到。

我有一个JavaScript对象,其中包含一些非英语字符。
我正在尝试使用以下代码将对象转换为Blob以进行下载。
当我点击下载内容并打开下载的JSON时,非英语字符变成了乱码。

这是一个简单的对象,如下所示:{name: "שלומית", last: "רעננה"}

function setJSONForDownload(obj) {
    obj = obj || []; // obj is the array of objects with non-english characters
    const length = obj.length;
    if (length) {
      const str = JSON.stringify(obj);
      const data = encode( str );

      const blob = new Blob( [ data ], {
        type: "application/json;charset=utf-8"
     });

      const url = URL.createObjectURL( blob );
      const downloadElem = document.getElementById('download');
      downloadElem.innerText = `Download ${length} pages scraped`;
      downloadElem.setAttribute( 'href', url );
      downloadElem.setAttribute( 'download', 'data.json' );
    }
    else {
      document.getElementById('download').innerText = `No data to download...`;
    }
}

function encode (s) {
  const out = [];
  for ( let i = 0; i < s.length; i++ ) {
    out[i] = s.charCodeAt(i);
  }
  return new Uint8Array(out);
}

你能分享一下你在blob中设置的数据吗?它只是一个带有非英文字符的文本,还是其他什么东西? - OriEng
请查看链接:https://dev59.com/G1QJ5IYBdhLWcg3wg2X4#53774151 - Shubham Dixit
2
返回一个 Uint16Array()。字符编码是16位而不是8位。然后将类型设置为 "application/json;charset=utf-16" - Patrick Roberts
@OriEng 这是一个简单的对象,就像这个: {name: "שלומית", last: "רעננה"} - Loves2Develop
3个回答

38

你的encode函数有问题,它将charcodes转换为bytes。不要试图自己实现,只需使用编码API

const str = JSON.stringify(obj);
const bytes = new TextEncoder().encode(str);
const blob = new Blob([bytes], {
    type: "application/json;charset=utf-8"
});

根据你的个人头像和这段简单的代码,我相信你是一位魔术师 :) 非常好用,谢谢! - Loves2Develop
1
虽然这里不会有问题,因为它不会被使用,但请注意,即使不是明确禁止的,设置;charset=nnn也会使某些浏览器(至少是Safari)无效。最好不要设置它,因为它对内容没有任何影响。 - Kaiido
2
我之前错过了它,但是将DOMString转换为UTF-8是通过new Blob([DOMString])隐式完成的。 - Kaiido
@Kaiido 谢谢,我猜想可能有更简单的方法,但是没有花时间去查找。给你点赞! - Bergi
@RickyLevi 很可能是 Excel 的问题。众所周知,它在编码方面表现不佳... - Bergi
显示剩余2条评论

6

调用 new Blob([DOMString]) 将自动将您的 DOMString(UTF-16)转换为 UTF-8。

所以您只需要使用 new Blob( [JSON.stringify(obj)] )

setJSONForDownload([{ name: "שלומית", last: "רעננה"}]);

function setJSONForDownload(obj) {
  obj = obj || [];
  const length = obj.length;
  if (length) {

    // DOMString
    const str = JSON.stringify(obj);
    // text/plain;UTF-8
    const blob = new Blob([str]);
    
    const url = URL.createObjectURL(blob);
    const downloadElem = document.getElementById('download');
    downloadElem.innerText = `Download ${length} pages scraped`;
    downloadElem.setAttribute('href', url);
    downloadElem.setAttribute('download', 'data.json');
  } else {
    document.getElementById('download').innerText = `No data to download...`;
  }
}
<a id="download">dl</a>


1
我找到了一段很好的代码,解决了我的问题。
感谢'pascaldekloe' (https://gist.github.com/pascaldekloe/62546103a1576803dade9269ccf76330)。
只需将编码方法更改为以下内容:
function encode(s) {
    var i = 0, bytes = new Uint8Array(s.length * 4);
    for (var ci = 0; ci != s.length; ci++) {
        var c = s.charCodeAt(ci);
        if (c < 128) {
            bytes[i++] = c;
            continue;
        }
        if (c < 2048) {
            bytes[i++] = c >> 6 | 192;
        } else {
            if (c > 0xd7ff && c < 0xdc00) {
                if (++ci >= s.length)
                    throw new Error('UTF-8 encode: incomplete surrogate pair');
                var c2 = s.charCodeAt(ci);
                if (c2 < 0xdc00 || c2 > 0xdfff)
                    throw new Error('UTF-8 encode: second surrogate character 0x' + c2.toString(16) + ' at index ' + ci + ' out of range');
                c = 0x10000 + ((c & 0x03ff) << 10) + (c2 & 0x03ff);
                bytes[i++] = c >> 18 | 240;
                bytes[i++] = c >> 12 & 63 | 128;
            } else bytes[i++] = c >> 12 | 224;
            bytes[i++] = c >> 6 & 63 | 128;
        }
        bytes[i++] = c & 63 | 128;
    }
    return bytes.subarray(0, i);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接