上节内容已经实现了客户端使用长轮询的方式获取消息的功能.
但是还没有展现到前端.本节内容将实现
1.展现消息到前端窗口.
2.客户端之间发送图片和文件.
3.文件上传时显示进度条
下面我们来实现上面3个功能.
1.展现消息到前端窗口.在展示前我们要解决一个问题.
问题1:我们先看看前端现在虽说能点击左边好友列表,发送消息给选择的好友,但是右边的消息窗口却一直不变.
如下.我给大神发一个111, 显示在这个window窗口,我在给小雨发一个 还是显示在这个窗口.这个肯定是不对的.应该实现得像webQQ和微信一样.点击到指定好友时,只显示和这个好友的消息纪录.
引入

和

那么我们先来解决这个问题
后端实现思路: 1.将所有的消息存到数据库里,切换到指定用户的时候,从数据库里把消息展示出来.这种方式是后台做的.对于聊天好像不太实用,占用空间.
前端实现思路: 1.为每一个好友创建一个消息窗口的div,默认都是hide隐藏.当选择指定的好友时,removeClass(hide)即可.这种方式可以,实现起来也不难.
2.使用jquery中的data.每一个元素有一个data属性,可以给data属性设置值也可以取 data属性的值.如图
引入

这种方式可以使用.老师觉得这个data属性有些鸡肋!为什么说是鸡肋呢?因为你都用了jquery了,使用给消息窗口这个div元素设置data属性.存储 用户id:聊天内容 ,这个和创建一个字典没有任何区别.
3. 在js中创建一个全局字典.存储 消息类型:用户id:聊天内容 ,切换不同好友的时候,先把现有的消息内容dump到字典,然后loads 新好友的内容.
我们就采用前端实现思路的第3种方式. 在js中创建一个字典.
那么问题来了,从哪里入手.
当然从请求源开始.点击切换用户的函数.

我们看这个函数要做哪些?如图

功能是要实现如上图,但是顺序要调整下.我们应该在更改title之前把消息存起来.所以合理的函数应该是下面这样的.

结构理清楚了.开始实现.
现写一个全局字典,字典写到哪里都行.这里我们就写在$(document).ready(function{}) 的上面,如图:

接下来先把老的信息存到这个全局字典.新的消息取出来.
代码实现如图:

这时候我们来访问测试下:

和

这样我们就解决了问题1了.下面我们就把接收到的消息展示在前端
首先看下现在收到消息的处理方法:
function GetNewMsgs(){
$.getJSON("{% url 'get_new_msgs' %}",function(callback){
console.log(callback);
GetNewMsgs();
});
}
只是打印.现在前端看下:

所以我们展示数据要有以下几步:
1.遍历上图中array[3]这个数组
2.分析每一个object里的from
3.判断如果from消息来源是我正在聊的对象也就是chat-box-window正在打开的对象,就把消息append到chat-box-window的html中
如果不是正在打开的聊天对象,那么就把消息append到全局字典消息from来源的ID下.
4.在消息展示之前还要把每一个object拼接成html的代码.
就是把饭回来的object 字典,整理成html代码

鉴于把object字典整理成html代码,需要一定的代码量,那我们就把这个写成一个函数.叫做ParseNewMsgs(),于是GetNewMsgs()函数如下:

上图说是for循环字典,其实不是的,这里后端返回来的是一个object的列表.for循环列表时取到的是下表
我们这里要温故下html中for循环的知识了.自己看
最终代码如下

然后前端接收信息看下结果如图:

接下来就是把拼接html.
我们先处理正在聊天对象发过来的消息拼接.代码如图:

我们先来测试下:

我们看上图聊天已经展示到消息窗口了,但还存在上图所述问题.
除了上面的问题,你如果是在windows下进行测试,会经常出现一方发给另外一方能收到,而另外一方发给你收不到.
查看后台你会发现一个规律,页面刷新一次,然后在发送消息,后台就会有断开报错.报错原因就是socket断开了,你在发送消息.
这个原因可能是windows,具体不详.记住就好.在Linux下没有这种问题.
我们来看看前端代码改的两处.
下面我们来拼接处理,不是正在聊的对象,如何把消息存储在全局字典里.

我们测试看下结果:
首先大神的账户和小雨聊.这时候说明大神聊天的对象不是"测试账户",如图

在用测试账户给大神发信息如图:

这时候大神的账户把聊天对象切换到测试账户,看看能不能收到消息,如图

我们看成功了.那么我们还要实现一个功能,就是显示新消息的个数.如图:

实现思路:
<li contact-type='single' contact-id='{{friend.id}}' onclick="OpenChatWindow(this)" class="list-group-item">
<span class="badge">14</span>
<span class="contact-name">{{friend.name}}</span>
</li>
我们可以把 <span class="badge">14</span> 默认添加hide隐藏样式,另外把缺省14改成0.

我们找到了元素,记得是元素,不是docment对象,所以不能是使用jquery的方法.要把找到的元素变成docment对象,之前我们用过就是加上$()即可
于是我们可以这样实现:

你可能会问既然元素都找到了,为啥还要转换成docment类呢?
因为我们要用的不是这个li元素 ,而是li元素下的span元素,我们要对这个元素进行更改.

我们总结下:
var li_ele = $(".list-group li[contact-type='single']").filter("li[contact-id='3']")[0]
$(li_ele).find(".badge")
我们接下来就是对这找到的内容进行操作.

接下来我们发送测试看看效果.

至此消息数量的提示已经完成.但是当我们切换有消息的好友的时候,是不是应该把消息数从新改成 hide并且数量改成0.
因此我们应该修改OpenChatWindow()函数来实现

接下来测试如下:

和

至此单对单的聊天的几本功能实现了,下面我们来实现下群组聊天是该如何实现?
群组聊天和单人聊天有什么不一样.
后台,处理后发给群里所有人
前端,收消息时前端展示时逻辑有些不一样
我们1对1 时,消息展示在好友处是根据后台返回的object 字典里的from id取到的.
而群组我们就不能根据from id来取了.而是根据to 的id来取到群组的id,from只是发消息的人.
因此我们改动两处,1,是sendmsg调用的后台视图函数send_msg.
2.是前端获取消息里的消息拼接处理方法
function GetNewMsgs(){
$.getJSON("{% url 'get_new_msgs' %}",function(callback){
console.log(callback);
ParseNewMsgs(callback); //重点是要改这个函数
GetNewMsgs();
});
}
我们先来修改后台的视图函数,我们先看看现在send_msg视图函数是怎么处理消息的.

我们把视图函数修改成如下:

后端的视图函数就做这些修改.
接下来就是前端取数据了.
前端取数据需要注意的就是 消息object里的key to是消息展示的位置.from只是显示在群租聊天框的人员姓名.
我们先看下目前的消息处理函数

于是把前端函数改成如下:

下面我们进行测试



至此分组聊天的功能我们已经实现了.
但是有一个问题我们要解决,就是在消息框中显示的发消息的人是ID而不是人员名称。这里要改成人员名称。
简单,我们可以在发送消息时,消息字典里加入发送人员的from_name属性。
如下:

然后,前端在拼接消息时也修改如下:

上面的问题解决了,几本是可以聊天了,但是实际测试下 来感觉会丢失消息,原因可能是socket的也可能是长轮询的方式有点问题,具体没有深入研究。
先不管,我们在来实现一个功能,文件上传.
思路: 上传用post 用ajax .后台处理的URL和和处理消息的URL是同一个。为什么可以是同一个。因为对于用户来说都是发送消息。
那么我们就要修改后台处理get_msgs()函数了。判断前端传来的是图片还是文本信息。如果是文本就不用说了,存到队列中。
如果是图片那么就把图片的url存到信息中。为了好判断是图片还是文本,我们就在object消息字典中再加一个key,来表示发送消息到底是什么类型。
那么我们开始动手,顺序依然是从请求源开始。 触发这个上传文件请求的是哪里。是在消息页面中。我们想图片就不在消息里写入了。我们在emoj处写一个文件上传的前端代码。
如图:

代码如下:

显示的样式很不好看,如下:

看到小图标的样式了把,但是上面加了input的样子很丑,jquery里肯定有具有input type="file"的代码,并且好看点的。
我们稍微调整下,让它不那么难看,然后在实现功能。

然后在看看样子:

然后把上面代码加上onclock事件
<div class="chat-box-emoj">
<!--emoj-->
<div class="col-md-3">
<input name="filename" type="file">
</div>
<div class="col-md-2">
<span class="glyphicon glyphicon-upload" onclick="FileUpload()"></span>
</div>
</div>
接下来我们就来写 FileUpload()函数,写之前我们来会想下:
我们在bbs系统里上传图片用的form表单。form表单会有一个问题,就是上传成功后会刷新页面。
这里我们当然不希望它刷新页面了。那么应该怎么办?就是不用form表单二用ajax异步提交文件。
什么是ajax异步提交文件。之前ajax不支持提交文件。那时候采用在页面用一个afreem,就是在页面里搞一个其他页面框架,内连东西。这个内连框架和服务器端保持长连接,总之浪费资源又恶心。
但是现在jquery的ajax支持异步传输文件了。
我们就把下面这段代码作为,ajax异步上传文件的范例记住。不会就来找把。
在写之前,我们来回忆下,之前在使用form上传文件时,前端form又两点需要注意的:1.必须是post形式才能上传文件 2。必须又一个enctype="multipart/form-data" 才能进行文件上传。
如图:

同样我们用ajax上传是不是也要又这两个限制条件?是的,顺便我们来说明下ajax如何实现异步文件上传的。
首先我们知道form是可以上传文件的。那么ajax实现文件上传的思路就是,在js中实例化一个form对象,把相关数据都塞进这个form实例中。
然后在通过ajax把这个form对象传到服务器上。
于是范例代码如下:

看看上图,思路是不是清晰了很多
下面我们来看看到底塞进formdata实例中的是什么东西。
首先要知道 $("#file_test")其实查找的是input type="file"这个标签。所以我们先给之前代码的input加一个id="file_upload"
<input id="file_upload" name="filename" type="file">
然后访问,打印下看看到底是个什么东西,

就是把上面的对象给放到formdata实例里。
上图内容要仔细看了。对于理解后面的操作有帮助。
我们在看下ajax异步上传文件代码中有一句需要理解的:

而上面我们提到的form表单提交后台的两个条件。就是通过下图中的两处实现的。你就记住这是必须加的

最终前端ajax上传文件的代码如下:
function UploadFile(){
var formDate = new FormData();
console.log($("#file_upload")[0].files[0]);
formDate.append('file',$('#file_upload')[0].files[0]);
$.ajax({
url: "{% url 'file_upload' %}",
type: 'POST',
data : formDate,
processDate: false, //tell jQuery not to process the data
// 默认提交的post或者get请求会把后面的参数压缩成url参数专用的urlinto的格式,然后发送给后台。
// 但是我们这里发送的是文件,所以就上面的设置就告诉浏览器不用压缩了
contentType: false, //tell jQuery not to set contentType
// 不要加什么请求头,说白了就是原来是什么样就是什么样,不要给formDate加其他内容了。
success: function(data){
console.log(data);
//alert(data);
}
});
}
这个时候你就通过ajax提交了,提交后后台如何处理呢?必定它和form 提交的方式不一样。
下面是不是该修改URL了, 请求源 -> url -> 视图函数 -> 前端html
urlpatterns = [
# url(r'^$/', views.acc_login,name='login' ), 这种写法错误,r'^$/'要改成r'^$'
url(r'^$', views.dashboard,name='chat_dashboard' ),
url(r'^msg_send/$',views.send_msg,name='send_msg' ),
url(r'^new_msgs/$',views.get_new_msgs,name='get_new_msgs' ),
url(r'^file_upload/$',views.file_upload,name='file_upload' ),
]
然后在views.py中写一个file_upload视图
def file_upload(request):
print(request.POST)
return HttpResponse('dddddd')
先上传一个文件,查看下打印结果:
function UploadFiles(){
var formData = new FormData();
console.log($('#file_upload')[0].files[0]);
formData.append('file',$('#file_upload')[0].files[0]);
$.ajax({
url: "{% url 'file_uploads' %}",
type: 'POST',
data : formData,
processDate: false, //tell jQuery not to process the data
contentType: false, //tell jQuery not to set contentType
success: function(data){
console.log(data);
//alert(data);
}
});
}
结果我们测试有报错,chrome报错如下:

火狐浏览器报错如下:

按照火狐浏览器报出的错误查找到一片文章如下:
平时做表单都是跳转提交的,但是今天要做一个ajax图片异步上传,
网上搜索了下,方法都比较老了,居然还有用flash的,
普通的表单上传通过jquery的serialize()转换成querystring后就可以直接ajax post 上传,但是碰到文件上传就不奏效了,型号html5有个方法FormData()可以实现上传,
我写的代码如下:
function upThumbSubmit() {
if(!window.FormData) {
alert('your brower is too old');
return false;
}
var formData = new FormData($( "#upForm" )[0]);
$.ajax({
url:'?c=api&a=upload',
type:'post',
data:formData,
dataType:'json',
success:function(data){
alert(data);
return false;
}
});
}
但是报错了,错误如下
TypeError: 'append' called on an object that does not implement interface FormData.
既然浏览器明明显示支持formdata,为何这里显示append不是formdata接口呢?
答案只可能是jquery重载了formdata
在里面加上2个option,就好了,正确代码如下
function upThumbSubmit() {
if(!window.FormData) {
alert('your brower is too old');
return false;
}
var formData = new FormData($( "#upForm" )[0]);
$.ajax({
url:'?c=api&a=upload',
type:'post',
data:formData,
processData: false,
contentType: false,
dataType:'json',
success:function(data){
alert(data);
return false;
}
});
}
才发现自己写错了,如下:

这个小错误,我整了3小时。
还好解决了,更改后,上传文件测试,看看后台打印什么?

结果为空,那是不是ajax没把文件传过来。当然不是,是文件不能用request.POST获取,而是用request.FILES
更改视图函数如下:
def file_upload(request):
print(request.POST,request.FILES)
print("22222")
return HttpResponse('dddddd')
在此上传打印如下:

我们看文件句柄已经传给后台了。那么接下来就修改视图函数把文件存下来就行了。
def file_upload(request):
print(request.POST,request.FILES)
file_obj = request.FILES.get('file')
new_file_name = "uploads/%s"%file_obj.name
with open(new_file_name,"wb+") as new_file_obj:
for chunk in file_obj.chunks():
new_file_obj.write(chunk)
return HttpResponse('--upload success---')
后台异步接收文件,请参照《python2.0_day21_bbs系统评论自动加载+文章创建》一节中的解释。
然后我们前端上传,看看后端uploads目录下有没有该文件。如图

哈哈,文件已经上传成功了。
现在是实现了异步上传文件。但是我们是不是想看到文件上传的进度。如何实现呢?
接下来还有两个功能,一个是上传文件的进度条显示,
一个是把图片显示到聊天窗口,这里不想看了。2周后再补上。因为我已经等不及看主机管理章节了。因为那是我现在能用到的
废话不多说,咱开始。