Swoole从入门到入土(19)——WebSocket服务器[文件传输]

要利用WebSocket进行文件传输,我们需要讨论两种情况,分别是:发送方可以是客户端,和 发送方是服务端。

1、发送方是客户端 

1)服务端接收

$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
    switch ($frame->opcode)
    {
        case 0x09:
            $pongFrame = new Swoole\WebSocket\Frame;
            $pongFrame->opcode = WEBSOCKET_OPCODE_PONG;
            $server->push($frame->fd, $pongFrame);
            echo "pone\n";
            break;
        case 0x08:
            echo "Close frame received: Code {$frame->code} Reason {$frame->reason}\n";
            break;
        case 0x01:
            echo "Text string\n";
            break;
        case 0x02:
            echo "Binary data\n";
            //服务端在这里接收,$frame->data即是客户端发过来的文件二进制数据
            $server->push($frame->fd,$frame->data,WEBSOCKET_OPCODE_BINARY);
            break;
        default:
            $server->push($frame->fd, $frame->data);
            break;
    }
});

2) 客户端(Javascript)发送

 form.on('submit(fileSend)',function(data){
    var jqFile=$("#file");
    
    try
    {
      var file=jqFile.get(0).files[0];
      var filesize=file.size;
      var reader=new FileReader();
      //reader.readAsBinaryString(file);
      //这里可以通过slice函数,对文件进行分割,多次传送
      var blob=file.slice(0,filesize);
      reader.readAsArrayBuffer(blob);
      
      reader.onload=function(e){
        var arrBuf=e.target.result;
        oWs.send(arrBuf);
      };
    }catch(e){
      layer.msg("文件异常");
    }
    
    return false;
  });

到这里,有一点需要进行强调,利用slice对文件进行分割发送,多次send将文件内容多次传输到接收端,websocket协议会保证先发的先到达,后发的后达到,不会存在乱序问题。 

2、发送方是服务端

1) 服务端发送

上面的例子已经提到,利用服务端的push可以将文件二进制数据推送到客户端。

$server->push($frame->fd,$frame->data,WEBSOCKET_OPCODE_BINARY);

2)客户端接收

这一部分,就有比较多的讨论内容。首先,服务端将文件数据以二进制的形式传递到客户端,客户端可以选择以blob或arraybuffer方式接收。

下面我们指定以arraybuffer方式接收:

oWs=new WebSocket(url);
oWs.binaryType="arraybuffer";

接下来,如果arraybuffer存储的是字符串数据,我们面临的就是要判断这些二进制数是是GBK还是UTF-8,并转成对应的string。

//传入arraybuffer,判断是否utf8
function IsTextUtf8(inputStream)
{
  var byteArray=new Uint8Array(inputStream);

  var encodingBytesCount = 0;
  var allTextsAreASCIIChars =  true ;
 
  for  ( var  i = 0; i < byteArray.length; i++)
  {
    var current = byteArray[i];
 
    if  ((current & 0x80) == 0x80) allTextsAreASCIIChars =  false ;
         
    if  (encodingBytesCount == 0)
    {
      if  ((current & 0x80) == 0) continue ;
 
      if  ((current & 0xC0) == 0xC0)
      {
        encodingBytesCount = 1;
        current <<= 2;
 
        while  ((current & 0x80) == 0x80)
        {
          current <<= 1;
          encodingBytesCount++;
        }
      }                   
      else
      {
        // Invalid bits structure for UTF8 encoding rule.
        return  false ;
      }
    }               
    else
    {
      if  ((current & 0xC0) == 0x80)
      {                       
        encodingBytesCount--;
      }
      else
      {
        return  false ;
      }
    }
  }
 
  if  (encodingBytesCount != 0)  return  false ;
  
  return  !allTextsAreASCIIChars; 
}

//传入arraybuffer,判断是否GBK
function IsTextGbk(inputStream)
{
  var byteArray=new Uint8Array(inputStream);
  var nBytes = 0;//GBK可用1-2个字bai节编码,中文两个 ,英文一个
  var chr = byteArray[0];
  var bAllAscii = true; //如果全部都是ASCII,
  for (var i = 0; i<byteArray.length; i++)
  {
    chr = byteArray[i];
    if ((chr & 0x80) != 0 && nBytes == 0)    // 判断是否ASCII编码,如果不是,说明有可能是GBK
    {
        bAllAscii = false;
    }
    if (nBytes == 0)
    {
      if (chr >= 0x80)
      {
        if (chr >= 0x81 && chr <= 0xFE)
        {
          nBytes = +2;
        }
        else
        {
          return false;
        }
        
        nBytes--;
      }
    }
    else
    {
      if (chr < 0x40 || chr>0xFE) return false;
      nBytes--;
    }//else end
  }
  
  if (nBytes != 0) return false;

  if (bAllAscii) return true;

  return true;
}

/*
以对应字符集读出arraybuffer的内容并转为字符串
u:arraybuffer
f:回调函数,参数为string
encoding:可选gbk和utf-8
*/
function ab2str(u,f,encoding) {
   var b = new Blob([u]);
   var r = new FileReader();
    r.readAsText(b, encoding);
    r.onload = function (){if(f)f.call(null,r.result)}
}
oWs.onmessage=function(e){
      var d;

      if(typeof e.data=="object")
      {
        if(IsTextUtf8(e.data))
        {
            ab2str(e.data,function(str){
              d="接收UTF-8:"+str;
            },"utf-8");
            return;
        }
        else if(IsTextGbk(e.data))
        {
          ab2str(e.data,function(str){
              d="接收GBK:"+str;
          },"gbk");
          return;
        }
        else
        {
          var arr=new Uint8Array(e.data);
          var str='';
          for(var i=0;i<arr.length;i++)
          {
            str+=String.fromCharCode(arr[i]);
          }
          d="接收对象埂进制数据:"+str;
        }
      }
      else d="接收文本数据:"+e.data;

};
 

       以上,就是关于文件传输的基本操作。我们可以设定自己的机制,连续传输图片数据,不断画到canvas上,造成小电影即视感;或者采用对传输的二进制数据进行base64编码方法进行传递。办法有很多,期待大家多多练习。本章到此结束。

完整的代码,大家可以看这里:传送门

下一篇:Swoole从入门到入土(20)——WebSocket服务器[协程版本]

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值