愛奇藝彈幕解碼 JS+ArrayBuffer+ungzip+utf8ArrayToStr

前文

解碼愛奇藝彈幕爲明文字符串,涉及ArrayBuffer、gzip、字符數組轉換,主要是前端部分範疇,不少東西不太瞭解,作此記錄。javascript

某劇彈幕資源地址php

http://cmts.iqiyi.com/bullet/77/00/874217700_300_3.z?business=danmu&is_iqiyi=true&is_video_page=true&tvid=874217700&albumid=205025001&categoryid=2

嘗試請求這個地址,發現資源結果是這樣兒的,而很是規的xml/Json。前端

 

最後抓包,在JS中xhr請求完畢後處理的邏輯進行調試。java

http://static.iqiyi.com/js/player_v1/skin.default.af360b73f3e281095e89.js

可悲的是測試了許久卻始終走不進這段代碼。node

最後發現同解碼邏輯一致還有其餘幾處邏輯,經測試,拖一拖進度條開關彈幕等待便可進入這段調試斷點。jquery

這裏首先異步請求後端拿到彈幕編碼資源,將其轉換成爲了Unit8Array的資源類型,以後將其ungzip,再之將其轉換成string便可得到彈幕XML。webpack

 

即分三步獲得彈幕明文:git

1.得到Unit8Array資源類型

愛奇藝原代碼是github

var r = new Uint8Array(i)

令我沒有想到的是這個函數是javascript內置的,也白白在這上面耗費了不少時間。web

這裏 變量 i 被賦值的是異步的請求結果

i = t.response

i的類型是ArrayBuffer,也就意味着返回結果一樣這樣,而Jquery的ajax資源類型不包含ArrayBuffer,返回的Text對應不上,轉換貌似也有些問題,因此也就沒有采用,最後採用原生XHR請求訪問。

// 字符串轉爲ArrayBuffer對象,參數爲字符串
function str2ab(str) {
    var buf = new ArrayBuffer(str.length*2); // 每一個字符佔用2個字節
    var bufView = new Uint16Array(buf);
    for (var i=0, strLen=str.length; i<strLen; i++) {
         bufView[i] = str.charCodeAt(i);
    }
    return buf;
}

該代碼將text轉換爲ArrayBuffer,摘自這裏

實現簡單的XHR請求,設定返回資源爲 ArrayBuffer。避免跨域,資源將由gzip.php代理請求。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/gzip.php', true);
xhr.responseType = 'arraybuffer';
 
xhr.onload = function(e) {
  console.log(this.response)
};
 
xhr.send()

測試返回資源類型是OK的,只須要再將其轉換爲Uint8Array就好。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/gzip.php', true);
xhr.responseType = 'arraybuffer';
 
xhr.onload = function(e) {
  // response is unsigned 8 bit integer
  var responseArray = new Uint8Array(this.response);
  console.log(responseArray);
};
 
xhr.send()

 

2.ungzip解壓數據

這裏實現拿到的數組進行ungzip解壓。

這裏無法直接用愛奇藝的代碼了,webpack打包的不成樣子,無法剝離。直接引用現成的庫。

https://raw.githubusercontent.com/nodeca/pako/master/dist/pako.js

將其下載到項目目錄裏再使用,不然出現跨域沒法執行。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/gzip.php', true);
xhr.responseType = 'arraybuffer';
 
xhr.onload = function(e) {
  // response is unsigned 8 bit integer
  var responseArray = new Uint8Array(this.response); 
  console.log(pako.ungzip(responseArray));
};
 
xhr.send()

解壓以後數據量會變的很大。

3.Unit8ArrayToStr

最後咱們再將其轉換爲肉眼可識別的XML文本。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/gzip.php', true);
xhr.responseType = 'arraybuffer';
 
xhr.onload = function(e) {
  // response is unsigned 8 bit integer
  var responseArray  = new Uint8Array(this.response); 
  var responseString = new TextDecoder().decode(pako.ungzip(responseArray));
  responseString = responseString.replace(/&#\d{2};/g, "");
  console.log(responseString);
};
 
xhr.send()

 

這裏TextDecoder也是JavaScript內置的功能類。

最終返回結果

至此,彈幕解碼完畢。

愛奇藝utf8ArrayToStr 轉換剝離代碼

function utf8ArrayToStr(e) {
	var t, a, i, r, n, o;
	for (t = "",
	i = e.length,
	a = 0; a < i; )
		switch ((r = e[a++]) >> 4) {
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
		case 7:
			t += String.fromCharCode(r);
			break;
		case 12:
		case 13:
			n = e[a++],
			t += String.fromCharCode((31 & r) << 6 | 63 & n);
			break;
		case 14:
			n = e[a++],
			o = e[a++],
			t += String.fromCharCode((15 & r) << 12 | (63 & n) << 6 | (63 & o) << 0)
		}
	return t
}

 

後文

這個解碼即如此,反向來看這個編碼而且壓縮以後體積會小不少,很適合一些大數據量場景向後端提交時使用。

 

擴展文章

TextEncoder

Conversion between UTF-8 ArrayBuffer and String

使用jQuery AJAX讀取二進制數據

Http請求中Content-Type講解

javascript中string轉UTF8格式byte數組

READING BINARY DATA USING JQUERY AJAX

pako.js對數據進行gzip壓縮傳遞到後臺解析,解決數據量大的請求問題

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息