利用Node.js將圖片流實(shí)時(shí)顯示在瀏覽器

實(shí)現(xiàn)功能:將socket.io服務(wù)器的二進(jìn)制圖片流利用img標(biāo)簽顯示在瀏覽器端弹灭,形成視頻铐尚。
所用到的知識(shí):WebSocket協(xié)議裳食,Node.js的Socket.io類(lèi)庫(kù)玫恳,瀏覽器的對(duì)象URL機(jī)制辨赐。

WebSocket協(xié)議

WebSocket是HTML5的一種新協(xié)議, 它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工通信京办。以前很多網(wǎng)站為了實(shí)現(xiàn)實(shí)時(shí)通信掀序,所用到的技術(shù)都是輪詢(xún),輪詢(xún)是在特定的時(shí)間間隔惭婿,由瀏覽器對(duì)服務(wù)器發(fā)出http請(qǐng)求不恭,然后由服務(wù)器返回最新的數(shù)據(jù)給瀏覽器。很顯然财饥,這種傳統(tǒng)模式的http請(qǐng)求需要瀏覽器端不斷的發(fā)出請(qǐng)求换吧,http請(qǐng)求的header是非常長(zhǎng)的,如果只是要發(fā)一個(gè)很小的數(shù)據(jù)佑力,會(huì)非常浪費(fèi)帶寬式散。在WebSocket API,瀏覽器和服務(wù)器只需要做一個(gè)握手的動(dòng)作就能實(shí)現(xiàn)全雙工通訊打颤。

Socket.io類(lèi)庫(kù)

瀏覽器的兼容問(wèn)題以及網(wǎng)絡(luò)中間物(代理服務(wù)暴拄、防火墻)問(wèn)題不支持WebSocket,這時(shí)Socket.io的出現(xiàn)就是為了完善這些問(wèn)題编饺。Socket.io是一個(gè)簡(jiǎn)單的小類(lèi)庫(kù)乖篷,該類(lèi)庫(kù)實(shí)現(xiàn)的功能類(lèi)似于Node.js中的net模塊實(shí)現(xiàn)的功能,這些功能包括WebSocket通信透且、XHR輪詢(xún)撕蔼、JSONP輪詢(xún)等豁鲤。該類(lèi)庫(kù)的一個(gè)顯著特征是在服務(wù)器端與瀏覽器之間提供一共享接口,也就是說(shuō)鲸沮,當(dāng)客戶(hù)端與服務(wù)端建立連接后琳骡,在處理消息時(shí),開(kāi)發(fā)者可以在客戶(hù)端使用服務(wù)器端JavaScript代碼讼溺。
下面是個(gè)小案例:

// socket.js
var http = require('http');
var sio = require('socket.io');
var fs = require('fs');
var server = http.createServer(function(req,res){
    res.writeHead(200,{'Content-type':'text/html'});
    res.end(fs.readFileSync('./index.html'));
});
server.listen(1337);
var socket = sio.listen(server);
socket.on('connectiion',function(socket){
    console.log('客戶(hù)端建立連接');
    socket.send('你好');
    socket.on('message',function(msg){
        console.log('接收到一個(gè)消息:',msg);
    });
    socket.on('disconnect',function(){
        console.log('客戶(hù)端斷開(kāi)連接');
    });
});

// index.html
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://cdn.bootcss.com/socket.io/1.7.3/socket.io.js"></script>
    <script>
        var socket = io.connect();
        socket.on('message',function(data){
            console.log(data);
            socket.send('消息已接收到');
        });
        socket.on('disconnect',function(){
            console.log('服務(wù)器斷開(kāi)連接');
        });
    </script>
</head>
<body>
</body>
</html>

瀏覽器的對(duì)象URL機(jī)制

對(duì)象URL機(jī)制--后臺(tái)服務(wù)器直接發(fā)送JPEG圖像數(shù)據(jù)的原始二進(jìn)制數(shù)據(jù)楣号,瀏覽器收到數(shù)據(jù)后把圖像數(shù)據(jù)解析成img元素能過(guò)引用的文件,能夠有效降低圖像數(shù)據(jù)傳輸?shù)恼加脦挕?/p>

objectURL=window.URL.createObjectURL(blob);

blob參數(shù)是一個(gè)文件對(duì)象或者Blob對(duì)象怒坯,Blob對(duì)象代表文件的二進(jìn)制數(shù)據(jù)炫狱,objectURL是生成的對(duì)象URL,這個(gè)URL的格式為"blob"開(kāi)頭的鏈接對(duì)象剔猿,一個(gè)對(duì)象的URL長(zhǎng)這個(gè)樣子:

![](blob:http%3A//localhost%3A4000/ffd23149-960f-4555-92a5-c3e1d6e9d1d9)

具體用法參考http://www.cnblogs.com/liulangmao/p/4262565.html
還有一種是對(duì)象URI機(jī)制视译,一般而言,img標(biāo)簽無(wú)法把圖像的二進(jìn)制數(shù)據(jù)解析成圖片文件归敬,這是由瀏覽器防止用戶(hù)直接引用用戶(hù)計(jì)算機(jī)本地文件的安全機(jī)制決定的酷含,但是瀏覽器有種解析data URI數(shù)據(jù)的方式,我們可以利用這種特性來(lái)解析圖像汪茧。data URI是一種直接把文件嵌入HTML文檔的方案第美,其格式如下:

data:[<mediatype>][;base64],<data>

上述<data>部分就是文件原始二進(jìn)制數(shù)據(jù)的base64編碼數(shù)據(jù),但是這種編碼得到的數(shù)據(jù)比原始數(shù)據(jù)大了1/3陆爽,會(huì)導(dǎo)致數(shù)據(jù)傳輸占用帶寬加大,因此扳缕,我們采用對(duì)象URL機(jī)制慌闭。
下面是將socket.io服務(wù)器的二進(jìn)制數(shù)據(jù)傳輸給瀏覽器的代碼(‘圖片的二進(jìn)制數(shù)據(jù)可以自己將本地圖片的二進(jìn)制數(shù)據(jù)輸進(jìn)去’):

var http = require('http');
var sio = require('socket.io');
var fs = require('fs');
var server = http.createServer(function(req,res){
    res.writeHead(200,
        {
            'Content-type':'text/html'
        }
    );
    res.end(fs.readFileSync('./img.html'));
});

server.listen(4000);
var io = sio.listen(server);
socket.on('connection',function(socket){
   socket.emit('news',{shuju:'圖片的二進(jìn)制數(shù)據(jù)'});
})

瀏覽器端代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://cdn.bootcss.com/socket.io/1.7.3/socket.io.js"></script>
    <script>
        var socket = io.connect();
        socket.on('news',function(data){
            var blob = new Blob([data.shuju],{"type":"image\/jpeg"});
            var src = window.URL.createObjectURL(blob);
            var img = document.createElement('img');
            img.src = src;
            img.onload = function(){
                window.URL.revokeObjectURL(src);
            };
            document.body.appendChild(img);
        });
    </script>
</head>
<body>
    <div id="data"></div>
</body>
</html>

由于是用本地圖片測(cè)試的,路徑會(huì)有問(wèn)題躯舔,會(huì)導(dǎo)致瀏覽器端顯示不出來(lái)圖片可以在控制臺(tái)查看是否正確:


要想從http服務(wù)器傳輸圖片數(shù)據(jù)給socket.io服務(wù)器驴剔,可以用node.js的進(jìn)程和子進(jìn)程解決,將socket.io代碼設(shè)為http部分的子進(jìn)程粥庄,即可實(shí)現(xiàn)傳輸丧失。

// http.js
var http = require('http');
var cp = require('child_process');
var n= cp.fork(__dirname+'/socket.js');

var server = http.createServer(function(req,res){

}).listen(3000,"localhost",function(){
    n.send({shuju:'圖片二進(jìn)制數(shù)據(jù)'})});

//監(jiān)聽(tīng)事件
server.on('listening',function(){
    console.log('服務(wù)器開(kāi)始監(jiān)聽(tīng)');
    //server.close();
});
//建立連接
server.on('connection',function(socket){
    console.log('客戶(hù)端已連接');
});

//服務(wù)器關(guān)閉時(shí)響應(yīng)
server.on('close',function(){
    console.log('服務(wù)器已關(guān)閉');
});
//觸發(fā)錯(cuò)誤事件
server.on('error',function(){
    if(e.code=='EADDRINUSE'){ //端口被占用的錯(cuò)誤代碼為EADDRINUSE
        console.log('服務(wù)器端口已被占用');
    }
});

由于JPEG編碼得到的圖像數(shù)據(jù)大小均不同,且TCP協(xié)議只是保證接收端接受的的比特是按序的惜互,但不能保證每次接收的數(shù)據(jù)的就是發(fā)送端發(fā)送的完整數(shù)據(jù)布讹,可能只是其中的一部分。因此训堆,node.js服務(wù)器接收時(shí)不能用長(zhǎng)度來(lái)指定接收一幀JPEG圖像數(shù)據(jù)的Buffer描验。此處使用了JPEG數(shù)據(jù)的最后兩個(gè)ASCII碼的217和255來(lái)區(qū)分兩幀圖像數(shù)據(jù),并使用Buffer把之前接收到的二進(jìn)制數(shù)據(jù)緩存起來(lái)最后拼接完成完整的JPEG幀數(shù)據(jù)坑鱼。

socket.js
var http = require('http');
var sio = require('socket.io');
var fs = require('fs');
var server = http.createServer(function(req,res){
    res.writeHead(200,
        {
            'Content-type':'text/html'
        }
    );
    res.end(fs.readFileSync('./img.html'));
});

server.listen(3000);
var io = sio.listen(server);
process.on('message',function(m){
    var buffers=[];
    io.sockets.on('connection',function(socket){
        buffers.push[m.shuju];
        var n_shu= m.shuju;
        if(n_shu[n_shu.length-1]==217 && n_shu[n_shu.length-2]==255){
            var n_buffer = Buffer.concat(buffers);
            io.sockets.emit('news',m);
            buffers=[];  
        }
    });
});

瀏覽器端代碼不變膘流,以上便完成圖像的傳輸。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市呼股,隨后出現(xiàn)的幾起案子耕魄,更是在濱河造成了極大的恐慌,老刑警劉巖彭谁,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吸奴,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡马靠,警方通過(guò)查閱死者的電腦和手機(jī)奄抽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)甩鳄,“玉大人逞度,你說(shuō)我怎么就攤上這事∶羁校” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵揖赴,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我燥滑,道長(zhǎng)渐北,這世上最難降的妖魔是什么铭拧? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮搀菩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肪跋。我一直安慰自己,他們只是感情好州既,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著吴叶,像睡著了一般褥琐。 火紅的嫁衣襯著肌膚如雪敌呈。 梳的紋絲不亂的頭發(fā)上贸宏,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音磕洪,去河邊找鬼吭练。 笑死,一個(gè)胖子當(dāng)著我的面吹牛析显,可吹牛的內(nèi)容都是我干的鲫咽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼谷异,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼分尸!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起歹嘹,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤箩绍,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后尺上,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體材蛛,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年怎抛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了卑吭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡马绝,死狀恐怖豆赏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情富稻,我是刑警寧澤河绽,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站唉窃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏纹笼。R本人自食惡果不足惜纹份,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望廷痘。 院中可真熱鬧蔓涧,春花似錦、人聲如沸个绍。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至茉盏,卻和暖如春鉴未,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸠姨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工铜秆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讶迁。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓连茧,卻偏偏與公主長(zhǎng)得像巍糯,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子罚斗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容