Django+Nginx+uwsgi网站Channels+redis+daphne多人在线聊天实现粘贴上传图片

 在Django+Nginx+uwsgi网站Channels+redis+daphne多人在线的基础上(详见Django+Nginx+uwsgi网站使用Channels+redis+daphne实现简单的多人在线聊天及消息存储功能-CSDN博客),实现在输入框粘贴或打开本地图片,上传到网站后返回图片路径,以链接的形式将图片插入到输入框显示,并实现异步发送消息。具体效果如下图所示:

一、实现图片上传

实现图片上传客户端和服务器两边都要配置。

1.  客户端使用fetch实现图片上传

使用嵌入页面的javascript脚本实现fetch上传图片,主要代码如下:

        const csrftoken = document.querySelector('[name="csrfmiddlewaretoken"]').value;fetch('/chatjson/upload_image/',{method: 'POST',headers: {'X-CSRFToken': csrftoken},body: imgformData}).then(response => response.json()).then(data => {if (data.image_url) {//console.log('data image path::',data.image_url);const img = document.createElement('img');img.src = data.image_url;editor.appendChild(img);img.style.width = '300px';img.style.height = 'auto';img.style.objectFit = 'contain';img.style.float = 'none';} else {console.error('Error uploading image:', data.error);}}).catch((error) => {console.error('Error:', error);alert('Error uploading the image.');});
2. 服务器端配置
(1) urls.py设置

客户端fetch的路径为'/chatjson/upload_image/',需要在urls.py中配置路径解析,包括聊天页面的路径解析

from myapp import views as channelsview
urlpatterns = [
....path('chatexp/<str:room_name>/', channelsview.chatexp, name='chatexp'),path('chatjson/upload_image/', channelsview.upload_image_json, name='upload_json'),
]
(2) 视图设置 myapp/views.py

包括聊天页面视图响应函数chatexp和文件上传响应upload_image_json

from django.contrib.auth.decorators import login_required@login_required(login_url='/login/')
def chatexp(request,room_name):username = request.session.get('username','游客')msgs = ChatMessage.objects.filter(room=room_name).order_by('-create_time')[0:20]if request.method == 'POST':form = chatimgsForm(request.POST, request.FILES)if form.is_valid():image = form.save()#图片路径image_path = image.image.urlreturn render(request,"channels/chattingexp.html",{'room_name':room_name,'form':form, 'image_path':image_path, 'username':username, 'msgs':msgs})form = chatimgsForm()return render(request,"channels/chattingexp.html",{'room_name':room_name,'form':form, 'image_path':'未上传', 'username':username, 'msgs':msgs})from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
from django.conf import settings
import os@require_POST
@csrf_exempt
def upload_image_json(request):image_file = request.FILES['image']if image_file :upimg = chatimgs(image=image_file)upimg.save()#返回图片的绝对路径/home/...#image_path = upimg.image.path# 返回图片的相对路径/media/...image_path = upimg.image.urlreturn JsonResponse({'image_url': image_path})else:return JsonResponse({'error': 'No image received!!'}, status=400)

二、客户端配置

1. 聊天页面设置

chatexp视图函数调用聊天页面chattingexp.html,聊天页面输入框由可编辑的div实现,页面内javascript脚本监听输入框的粘贴事件,将其中的图片上传,返回路径,将图片以img元素的形式插入到输入框,字符串转换成文本插入。脚本还实现了打开本地图片文件,同样上传后返回路径,将图片以img元素的形式插入到输入框。然后发送消息,消息文本通过channels异步传输,因文本只有图片链接,提高了传输效率。主要代码如下:

{% extends "newdesign/newbase.html" %}{# 自定义过滤器startswith #}{% load django_bootstrap5 %}{% block mytitle %}<title>{{room_name}}号聊天室</title><style>.chat-window {max-width: 900px;height: 500px;overflow-y: scroll; /* 添加垂直滚动条 */margin: auto;background-color: #f1f1f1;border: 2px solid #09e3f7;border-radius: 5px;padding: 10px;}.chat-message {clear: both;overflow: hidden;margin-bottom: 10px;text-align: left;}.chat-message .message-content {border-radius: 5px;padding: 8px;max-width: 500px;float: left;clear: both;}.chat-message.right .message-content {background-color: #428bca;color: white;float: right;width:420px;}.chat-message.right .user-content {background-color: #f7e91d;border-radius:4px;color: black;float: right;width: auto;text-align: right;padding-left:10px;padding-right:10px;}.chat-message.left .message-content {background-color: #2ef3be;border-color: #ddd;float:left;width:420px;}.chat-message.left .user-content {background-color: #f7e91d;border-radius:4px;border-color: #ddd;float: left;width: auto;text-align: left;padding-left:8px;padding-right:8px;}.inputarea {display:flex;flex-direction: column;justify-content: center;align-items: center;width:900px;margin: 0 auto;}.replyinput {display: inline-block;width: 900px;min-height:120px;background-color: rgb(169, 228, 250);border:2px solid #09e3f7;border-radius: 10px;padding: 10px;font-size: 14px;text-align: left;}.replyarea {width: 900px;height:50px;margin:0 auto;}.sendImg-btn {float:left;border: 0px;background-color: transparent;}.reply-btn {float:right;}</style>{% endblock %}{% block maincontent %} <div class="container"><div id="chat-record" class="chat-window">{% for m in msgs reversed %}{% if m.username == request.user.username %}<div class="chat-message right"><div class="user-content">{{m.username}}</div><div class="message-content"><span>{{m.content|safe}}</span></div></div><br>{% else %}<div class="chat-message left"><div class="user-content">{{m.username}}</div><div class="message-content"><span>{{m.content|safe}}</span></div></div><br>{% endif %}{% endfor %}</div></div><br><form method='post' enctype="multipart/form-data"></form>{% csrf_token %}<div class="inputarea"><divclass="replyinput"contenteditable="true"id="chat-message-input"@focus="onFocusEditableDiv"></div><br><div class="replyarea">&nbsp;&nbsp;<button id="upload-btn">上传本地图片</button>&nbsp;&nbsp;<input type="file" id="file-input" accept="image/*"/><button class="reply-btn" id="chat-message-submit" type="primary" style="height:40px;background-color: #0d4de1;color:white;border-radius: 4px;">发送消息</button></div></div></form>{{ room_name|json_script:"room-name" }}{{ username|json_script:"username" }}<script>const editor = document.getElementById('chat-message-input'); editor.addEventListener('paste', function(event) {// 阻止默认粘贴操作event.preventDefault();const clipboardData = (event.clipboardData||window.clipboardData);let items = clipboardData.items;const csrftoken = document.querySelector('[name="csrfmiddlewaretoken"]').value;for (const item of items) {if  (item.kind === 'string') {item.getAsString((text) => {const regex = /<img src="(.*?)"/;const match = text.match(regex);if (match) {//document.execCommand('insertText', false, "link:<img src='"+match[1]+"'/>");//网页图片复制粘贴除了图片还带有图片链接,如果识别img链接插入图片会出现图片插入两次的问题//const img = document.createElement('img');//img.src = match[1];//editor.appendChild(img);//img.style.width = '300px';//img.style.height = 'auto';//img.style.objectFit = 'contain';//img.style.float = 'none';} else {document.execCommand('insertText', false, text);}})} else if (item.kind === 'file' && item.type.indexOf('image/') !== -1) {var imgfile = item.getAsFile();const imgformData = new FormData();imgformData.append('image',imgfile);imgformData.append('csrfmiddlewaretoken', csrftoken);fetch('/chatjson/upload_image/',{method: 'POST',headers: {'X-CSRFToken': csrftoken},body: imgformData}).then(response => response.json()).then(data => {if (data.image_url) {//console.log('data image path::',data.image_url);const img = document.createElement('img');img.src = data.image_url;editor.appendChild(img);img.style.width = '300px';img.style.height = 'auto';img.style.objectFit = 'contain';img.style.float = 'none';} else {console.error('Error uploading image:', data.error);}}).catch((error) => {console.error('Error:', error);alert('Error uploading the image.');});}}})window.onload = function() {var scrollableDiv = document.getElementById('chat-record');// 设置scrollTop使得滚动条向下翻scrollableDiv.scrollTop = scrollableDiv.scrollHeight;};const roomName = JSON.parse(document.getElementById('room-name').textContent);const username = JSON.parse(document.getElementById('username').textContent);const chatSocket = new WebSocket('wss://abc.com/ws/chat/' + roomName + '/');chatSocket.onmessage = function(e) {const data = JSON.parse(e.data);//data为收到的后端发出来的数据//console.log(data);if (data['message']) {if(data['username'] == username){document.querySelector('#chat-record').innerHTML += ('<div class="chat-message right"><div class="user-content">' + data['username'] + '</div><div class="message-content"><span>' +data['message'] + '</span></div></div><br>');}else{document.querySelector('#chat-record').innerHTML += ('<div class="chat-message left"><div class="user-content">' + data['username'] + '</div><div class="message-content"><span>' + data['message'] + '</span></div></div><br>');}} else {alert('消息为空!')}var scrollableDiv = document.getElementById('chat-record');// 设置scrollTop使得滚动条向下翻scrollableDiv.scrollTop = scrollableDiv.scrollHeight;};chatSocket.onclose = function(e) {console.error('聊天端口非正常关闭!');};document.querySelector('#chat-message-input').focus();document.querySelector('#chat-message-input').onkeyup = function(e) {if (e.keyCode === 13) {  // enter, returndocument.querySelector('#chat-message-submit').click();}};document.querySelector('#chat-message-submit').onclick = function(e) {const messageDivDom = document.querySelector('#chat-message-input');const message = messageDivDom.innerHTML;chatSocket.send(JSON.stringify({'message': message,'username':username}));messageDivDom.innerHTML = '';};//打开并上传本地文件document.getElementById('upload-btn').addEventListener('click', function () {const editor = document.getElementById('chat-message-input'); const fileInput = document.getElementById('file-input');const csrftoken = document.querySelector('[name="csrfmiddlewaretoken"]').value;const file = fileInput.files[0];const formData = new FormData();formData.append('csrfmiddlewaretoken', csrftoken);formData.append('image', file);fetch('/chatjson/upload_image/', {method: 'POST',headers: {'X-CSRFToken': csrftoken},body: formData}).then(response => response.json()).then(data => {if (data.image_url) {//console.log('data image path::',data.image_url);const img = document.createElement('img');img.src = data.image_url;editor.appendChild(img);img.style.width = '300px';img.style.height = 'auto';img.style.objectFit = 'contain';img.style.float = 'none';} else {console.error('Error uploading image:', data.error);}}).catch((error) => {console.error('Error:', error);alert('Error uploading the image.');});});</script>
{% endblock %}
2. 存在的问题

无法正确处理html和word内容,拷贝粘贴文字和图片混合内容粘贴显示都不太正常,单独复制粘贴图片没问题,文本内容粘贴时需要粘贴为纯文本。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/61863.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ —— 以真我之名 如飞花般绚丽 - 智能指针

目录 1. RAII和智能指针的设计思路 2. C标准库智能指针的使用 2.1 auto_ptr 2.2 unique_ptr 2.3 简单模拟实现auto_ptr和unique_ptr的核心功能 2.4 shared_ptr 2.4.1 make_shared 2.5 weak_ptr 2.6 shared_ptr的缺陷&#xff1a;循环引用问题 3. shared_ptr 和 unique_…

C/C++链接数据库(MySQL)超级详细指南

C/C链接数据库&#xff08;MySQL&#xff09;超级详细指南 在C/C编程中&#xff0c;与数据库进行交互是一项常见的任务。MySQL作为一个广泛使用的开源关系型数据库管理系统&#xff0c;提供了丰富的API供C/C开发者使用。本文将详细介绍如何在C/C程序中链接MySQL数据库&#xf…

vulnhub靶场之breakout

empire靶场2 前言 靶机&#xff1a;breakout 攻击&#xff1a;kali 续接上个靶场empire1的继续学习 主机发现 使用arp-scan扫描或者直接查看虚拟机的ip地址 信息收集 使用nmap扫描 端口80apache 2.4.51开启smb服务的两个端口139、445&#xff0c;版本4.6.2两个http服务采…

今天你学C++了吗?——C++中的类与对象(第二集)

♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ ✨✨✨✨✨✨ 个…

聚划算!一区算法!双分解+牛顿拉夫逊优化+深度学习!CEEMDAN-VMD-NRBO-Transformer多元时序预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CEEMDAN-Kmeans-VMD-NRBO-Transformer融合K均值聚类的数据双重分解牛顿-拉夫逊优化算法Transformer多元时间序列预测&#xff08;完整源码和数据&#xff09;运行环境Matlab2023b及以上。 2.CEEMDAN分解…

C++设计模式-中介者模式

动机(Motivation) 多个对象相互关联的情况&#xff0c;对象之间常常会维持一种复杂的引用关系&#xff0c;如果遇到一些需求的更改&#xff0c;这种直接的引用关系将面临不断的变化。在这种情况下&#xff0c;可以使用一种”中介对象“来管理对象间的关联关系&#xff0c;避免…

滑动窗口篇——如行云流水般的高效解法与智能之道(2)

前言&#xff1a; 上篇我们介绍了滑动窗口的含义并结合基础题型加以练习&#xff0c;本篇将以进阶难度的题目为索引&#xff0c;深化对于滑动窗口的运用与理解。 一. 将x减到0的最小操作数 题目链接&#xff1a;1658. 将 x 减到 0 的最小操作数 - 力扣&#xff08;LeetCode&am…

SCI论文部分题目

SC20243213 通过氢和氨集成增强Power-to-X灵活性和可持续成本降低&#xff1a;绿色实验室Skive案例研究 SC20243211 分析同时发电的综合系统中的能量和能量效率、环境影响和经济可行性&#xff0c;淡水、热水和天然气凝液 SC20243208 双燃料生物质-天然气系统中的氢、…

EG3D: Efficient Geometry-aware 3D Generative Adversarial Networks 学习笔记

1 Contributions 混合显式-隐式网络架构&#xff1a;提出了一种 Tri-plane 的3D表征方法&#xff0c;结合显式体素网格与隐式解码器的优点 速度快&#xff0c;内存效率高&#xff1b; 支持高分辨率生成&#xff0c;保持3D表征的灵活性和表达能力。与纯显式或隐式方法相比&#…

Android——连接MySQL(Java版)

Android——连接MySQL(Java版) 目录&#xff1a; Android——连接MySQL(Java版)一、JDBC1、什么是JDBC2、载入JDBC3、创建JDBC的工具类 二、使用数据库 一、JDBC 1、什么是JDBC JDBC全称Java Database Connectivity&#xff0c;译为Java语言连接数据库&#xff0c;是sun公司制…

c++中操作数据库的常用函数

在C中操作数据库&#xff0c;尤其是MySQL数据库&#xff0c;主要通过MySQL提供的C API或MySQL Connector/C库来实现。这些库提供了一系列的函数&#xff0c;使得开发者能够在C应用程序中执行数据库的连接、查询、更新、删除等操作。以下是C中操作MySQL数据库的一些常用函数&…

Java 获取本机 IP 地址的方法

文章目录 一、使用 InetAddress.getLocalHost二、遍历网络接口获取 在 Java 编程中&#xff0c;若有本机的 IP 地址的需求&#xff0c;小编来展示一下方法&#xff1a; 一、使用 InetAddress.getLocalHost 一是最基本的获取本机 IP 地址的方式。 示例代码&#xff1a; impo…

⭐ Unity 资源管理解决方案:Addressable_ Demo演示

一、使用Addressable插件的好处&#xff1a; 1.自动管理依赖关系 2.方便资源卸载 3.自带整合好的资源管理界面 4.支持远程资源加载和热更新 二、使用步骤 安装组件 1.创建资源分组 2.将资源加入资源组 3.打包资源 4.加载资源 三种方式可以加载 using System.Collections…

C++《二叉搜索树》

在初阶数据结构中我学习了树基础的概念以及了解了顺序结构的二叉树——堆和链式结构二叉树该如何实现&#xff0c;那么接下来我们将进一步的学习二叉树&#xff0c;在此会先后学习到二叉搜索树、AVL树、红黑树&#xff1b;通过这些的学习将让我们更易于理解后面set、map、哈希等…

使用go实现流式输出

流式输出的深度剖析 之前一直在调用openai的key&#xff0c;只是照着文档进行流式调用&#xff0c;也只知其确是流式与api有所不同&#xff0c;而未成体系深究其实现原理。 就以openai的官方流式输出为切入。 概述 流式输出&#xff08;Streaming Output&#xff09;是 HTT…

使用VisualStudio编写C++程序输出helloWorld

文章目录 1. C简介1.1 历史背景1.2 特点1.3 应用领域 2. 操作过程和代码2.1 打开Visual Studio(默认你下载了C的相关文件)2.2 创建新项目2.3 输入名字&#xff0c;创建2.4 右击源文件->添加->新建项2.5 命名好&#xff0c;进行添加2.6 输入代码2.7 输出结果 3. 总结 1. C…

万能的无人机锁定目标投放程序

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

LayaBox1.8.4实现UV滚动

实现思路&#xff1a; 在片元着色器采样时&#xff0c;增加一个随时间变化的偏移值&#xff0c;由于uv是一个二维向量所以加的偏移值也需要一个二维向量。注意&#xff1a;在Laya的 shader中除了0&#xff0c;输入其它数字必须输入带有小数的数字&#xff0c;否则报错 。 &quo…

Next.js- App Router 概览

#题引&#xff1a;我认为跟着官方文档学习不会走歪路 一&#xff1a;App Router与Page Router 在 v13 版本中&#xff0c;Next.js 引入了一个基于 React 服务器组件 构建的新的 App Router&#xff0c;而在这之前&#xff0c;Next.js 使用的是Page Router。 目录结构 pages …

Android studio与JS交互

文章目录 前言一、html二、使用步骤1.2.AS 总结 前言 最近在使用Android Studio的WebView,有些功能要AS与JS交互。 一、html html文件 <!DOCTYPE html> <html> <!--javascript--> <head><meta charset"utf-8"><title>Carson…