先上效果图: 用户登录以后,就可以像服务端发送文字,或者图片;输入文字的时候,点击发送就可以了,如果发送图片,点击发送图片,选择图片后,会自动发送图片到服务器端;
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Chat Client</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no, user-scalable=no">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="telephone=no" name="format-detection">
</head>
<body>
<div style="text-align:center">用户登录<br><br>
用户名:<input type="text" id="Im_Name" value="" style="width:200px;"><br><br>
密 码:<input type="text" id="Im_PassWord" value="" style="width:200px;"><br><br>
<input type="button" id="login" value="登录">
<br><br>
<br><br>
<br><br>
</div>
<div style="text-align:center">
<input type="text" id="socketAddress" value="ws://172.21.20.108:1984" style="width:200px; display:none">
发送文字:<textarea cols="10" style="width:300px;height:200px" id="textField"></textarea><br><br>
发送图片:
<img src="" onclick="F_Open_dialog()" />
<input type="file" id="Imagg_File" style="display:none" />
<br><br>
<input type="button" id="send" value="发送">
</div>
<div id="result">
<!-- 这里用来显示读取结果 -->
</div>
<div id="result1">
<!-- 这里用来显示读取结果 -->
</div>
</body>
</html>
<script type="text/javascript" src="js/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="js/reconnecting-websocket.js"></script>
<script type="text/javascript" src="js/offline.js"></script>
<script src="layer/layer.js"></script>
<script src="layer/extend/layer.ext.js"></script>
<script type="text/javascript">
var Im_Name=document.getElementById("Im_Name");
var Im_PassWord=document.getElementById("Im_PassWord");
var textField=document.getElementById("textField");
var send = document.getElementById("send");
var login = document.getElementById("login");
var result=document.getElementById("result");
var result1 = document.getElementById("result1");
var socketStatus = false;
var globalBufferIndex='';
var websocket = null, tick_heartpac;
var timerID = '';
var globaldataindex = '';
var globalBuffer = '';
var globalBuffer_REVICE = '';
var Message_data = "";
var segment = 50 * 1024;
function F_Open_dialog() {
document.getElementById("Imagg_File").click();
}
function print(text) {
result1.innerHTML = (new Date).getTime() + ": " + text + result1.innerHTML+"<br>";;
}
function initSocket(option) {
//服务器地址
layer.msg("建立连接中...");
var locate = window.location;
var url = option.url ? option.url : "ws://" + locate.hostname + ":" + locate.port + _get_basepath() + "/websocket";
//回调函数
var callback = option.callback;
if (typeof callback !== "function") {
layer.msg('callback 必须为函数');
return false;
}
//一些对浏览器的兼容已经在插件里面完成
websocket = null;
websocket = new ReconnectingWebSocket(url);
//连接发生错误的回调方法
websocket.onerror = function () {
socketStatus = false;
layer.msg("websocket.error");
};
//连接成功建立的回调方法
websocket.onopen = function (event) {
Heart(!0);
//websocket.send("User_Name|zhang")
socketStatus = true;
//t1 = setInterval(tanchuang, 3000);
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
if (event.data != "@heart")
callback(event.data);
}
//连接关闭的回调方法
websocket.onclose = function () {
Heart(!1);
socketStatus = false;
layer.msg("websocket Server error");
websocket.close();
//if (websocket.readyState == 3) websocket.close();
//setInterval(initSocket(option), 3000);
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
socketStatus = false;
websocket.close();
}
websocket.onerror = function (evnt) {
socketStatus = false;
websocket.close();
};
return websocket;
}
$(function () {
var option = {
url: $("#socketAddress").val(),
callback: function (msg) {
console.log(msg);
//处理业务逻辑
var msg_data = eval("(" + msg + ")");//转换为json对象
//$.each(msg_data, function (index, item) {
// $("#info").append(
// "<div>" + index + ":" + item.name + "</div>" +
// "<div>" + index + ":" + item.value + "</div><hr/>");
//});
//layer.msg("数据接收成功" + msg_data.Message);
if (msg_data.Message_Type == "0") {
print("[onmessage] '" + msg_data.Message + "'\n");
}
else if (msg_data.Message_Type == "999") {
layer.msg("登录成功\n");
localStorage.setItem("Im_Id", msg_data.Im_PassWord);
}
else if (msg_data.Message_Type == "-1") {
layer.msg("登录失败\n");
}
else if (msg_data.Message_Type == "1") {
unpackagedata(msg_data.Message);
}
}
};
initSocket(option);
send.onclick = function () {
if (localStorage.getItem("Im_Id") == "") {
layer.msg("您还没有登录!")
return;
}
alert(localStorage.getItem("Im_Id"));
var Message = textField.value;
Message_data = "{ 'Send_Id': '" + localStorage.getItem("Im_Id") + "', 'To_Id': '10067', 'Message_Type': '0', 'Message': '" + Message + "' }";
"" != Message && (websocket && 1 == websocket.readyState ? (Heart(!0), websocket.send(Message_data), print("[send] '" + Message + "'\n"), $("#textField").val(""), Heart(!0)) : layer.msg("连接已经断开!"))
};
login.onclick = function () {
var Im_Name1 = Im_Name.value;
var Im_PassWord1 = Im_PassWord.value;
Message_data = "{ 'Im_Name': '" + Im_Name1 + "', 'Im_PassWord': '" + Im_PassWord1 + "', 'Message_Type': '-1'}";
"" != Im_Name1 && (websocket && 1 == websocket.readyState ? (Heart(!0), websocket.send(Message_data), Heart(!0)) : layer.msg("连接已经断开!"))
};
})
function Heart(a) {
a ? tick_heartpac = setInterval(function () {
websocket.send("@heart")
},
3e4) : (clearInterval(tick_heartpac), tick_heartpac = null)
}
// 用于压缩图片的canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
// 瓦片canvas
var tCanvas = document.createElement("canvas");
var tctx = tCanvas.getContext("2d");
var maxsize = 100 * 1024;
var result = document.getElementById("result");
var input = document.getElementById("Imagg_File");
if (typeof FileReader === 'undefined') {
result.innerHTML = "抱歉,你的浏览器不支持 FileReader";
input.setAttribute('disabled', 'disabled');
} else {
input.addEventListener('change', readFile, false);
}
function readFile() {
var file = this.files[0];
// if (!/image\/\w+/.test(file.type)) {
// alert("请确保文件为图像类型");
// return false;
// }
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function() {
var result = this.result;
var img = new Image();
img.src = result;
//如果图片大小小于100kb,则直接上传
if (result.length <= maxsize) {
img = null;
sendpicdata(this.result);
return;
}
//图片加载完毕之后进行压缩,然后上传
if (img.complete) {
callback();
} else {
img.onload = callback;
}
function callback() {
var data = compress(img);
sendpicdata(data);
img = null;
}
};
}
function sendpicdata(txt) {
if (timerID == '') {
globaldataindex='';
globalBuffer='';
globalBuffer = packagedata(txt);
Message_data ="{ 'Send_Id': '" + localStorage.getItem("Im_Id") + "', 'To_Id': '10067', 'Message_Type': '1', 'Message': '" + globalBuffer[0] + "' }";
websocket.send(Message_data);
//layer.msg(globalBuffer[0]);
globalBufferIndex = 1;
timerID = setInterval(sendBufferInterval, 1000);
}
}
function sendBufferInterval() {
if (globalBufferIndex < (globalBuffer.length)) {
Message_data = "{ 'Send_Id': '" + localStorage.getItem("Im_Id") + "', 'To_Id': '10067', 'Message_Type': '1', 'Message': '" + globalBuffer[globalBufferIndex] + "'}";
websocket.send(Message_data);
//layer.msg(globalBuffer[globalBufferIndex]);
globalBufferIndex += 1;
}
else {
clearInterval(timerID);
timerID = '';
}
}
function hexToString(num) {
var val = "";
var str = num.toString(16);
if (str.length < 4) {
for (var i = 0; i < (4 - str.length) ; i++) {
val += '0';
}
}
val += str;
return val;
}
function packagedata(data) {
var buffer = new Array();
var length = data.length;
alert(length);
if (length > segment) {
var packagecount = 0;
var packagefloor = 0;
packagecount = Math.ceil(length / (segment));
packagefloor = Math.floor(length / (segment));
for (var i = 0; i < packagefloor; i++) {
buffer[i] = '0001';
buffer[i] += '0000';
buffer[i] += hexToString(packagecount);
buffer[i] += hexToString(i + 1);
buffer[i] += data.substr(segment * i, segment);
}
if (packagecount > packagefloor) {
buffer[packagecount - 1] = '0001';
buffer[packagecount - 1] += '0000';
buffer[packagecount - 1] += hexToString(packagecount);
buffer[packagecount - 1] += hexToString(packagecount);
buffer[packagecount - 1] += data.substr(segment * (packagecount - 1), length % (segment));
}
}
else {
buffer[0] = '0001';
buffer[0] += '0000';
buffer[0] += '0001';
buffer[0] += '0001';
buffer[0] += data;
}
return buffer;
}
function unpackagedata(buffer) {
var data = new Array();
data[0] = buffer.substr(0, 4);//0001
data[1] = buffer.substr(4, 4);//0000
data[2] = buffer.substr(8, 4);//0001
data[3] = buffer.substr(12, 4);//0001
data[4] = buffer.substr(16);
//layer.msg(buffer);
view(data);
}
function view(data) {
debugger;
if (data[2] == data[3]) {
if (globaldataindex > data[3]) {
globaldataindex = '';
}
else {
globaldataindex = data[3];
globalBuffer_REVICE += data[4];
//layer.msg(globalBuffer_REVICE);
//alert(globalBuffer_REVICE);
result1.innerHTML +='<img src="' + globalBuffer_REVICE + '" width="100" height="100" alt=""/>';
globalBuffer_REVICE = '';
globaldataindex = 'ffffffff';
}
}
else {
if (globaldataindex == data[3]) {
}
else {
alert(globalBuffer_REVICE);
globaldataindex = data[3];
globalBuffer_REVICE += data[4];
}
}
}
// 使用canvas对大图片进行压缩
function compress(img) {
var initSize = img.src.length;
var width = img.width;
var height = img.height;
//如果图片大于四百万像素,计算压缩比并将大小压至400万以下
var ratio;
if ((ratio = width * height / 4000000) > 1) {
ratio = Math.sqrt(ratio);
width /= ratio;
height /= ratio;
} else {
ratio = 1;
}
canvas.width = width;
canvas.height = height;
// 铺底色
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
//如果图片像素大于100万则使用瓦片绘制
var count;
if ((count = width * height / 1000000) > 1) {
count = ~~(Math.sqrt(count) + 1); //计算要分成多少块瓦片
// 计算每块瓦片的宽和高
var nw = ~~(width / count);
var nh = ~~(height / count);
tCanvas.width = nw;
tCanvas.height = nh;
for (var i = 0; i < count; i++) {
for (var j = 0; j < count; j++) {
tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);
ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
}
}
} else {
ctx.drawImage(img, 0, 0, width, height);
}
//进行最小压缩
var ndata = canvas.toDataURL('image/jpeg', 0.6);
console.log('压缩前:' + initSize);
console.log('压缩后:' + ndata.length);
console.log('压缩率:' + ~~(100 * (initSize - ndata.length) / initSize) + "%");
tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
return ndata;
}
</script>
图片过分的大的时候需要分段发送;也可以进行图片压缩;图片的分段和压缩借鉴了网上一些js代码,谢谢各位的无偿奉献;
服务端使用mina开发的;两个客户端通过服务端转发就可以聊天了。
如果大家有好的web端设计或者想法,可以联系。