2
5

More than 3 years have passed since last update.

Django Channelsを使ったチャットアプリで画像を送れるようにする

Posted at

作業環境

python 3.8.6
Django 3.1.5
channels 3.0.3

はじめに

DjangoとChannelsを使ってチャットアプリを作っているのですが、テキストだけでなく画像もチャットで送れるようにしたいと思い、色々試して辿り着いた方法をここに記します。

Channelsの導入方法や設定などは記していません。

チャットアプリを作る際に参考にさせていただいたサイトはこの記事の最後にリンクを貼っています。

結論

画像をBase64形式(文字列)に変換すれば、テキストと同じように画像をやり取りできる。

作業

HTML

chat.html
...
<div id="messages"></div>
<form id="chat-form">
  <input type="text" id="text-input">
  <input type="file" id="image-input">
</form>
...

★JavaScript(画像をBase64に変換)

chat.js
...
let imageBase64 = ""; // Base64形式に変換した画像を代入する変数

const imageInput = document.getElementById("image-input");

imageInput.addEventListener('change', (e) => {
  // 画像ファイルを取得し、画像データ(URL)を変数に代入
  const imageFile = e.target.files[0];
  const imageURL = window.URL.createObjectURL(imageFile);
  // img要素を作成し、src属性に画像のURLを指定
  const imageElement = new Image();
  imageElement.src = imageURL;

  imageElement.onload = function() {
    // canvas要素を作成し、img要素を描画
    const canvasElement = document.createElement('canvas');
    canvasElement.width = imageElement.width;
    canvasElement.height = imageElement.height;
    const canvasContext = canvasElement.getContext('2d');
    canvasContext.drawImage(imageElement, 0, 0);
    // canvas要素をbase64形式に変換
    imageBase64 = canvasElement.toDataURL("image/png");
  };
});
...
  • Base64形式に変換できるのはcanvas要素に描画されている内容
  • 画像をimg要素にすることでcanvas要素に描画できる

JavaScript(テキストと画像をwebsocketへ送信)

chat.js
...
const ws = new WebSocket( "ws://" + window.location.host + window.location.pathname );

const textInput = document.getElementById("text-input");

document.getElementById("chat-form").addEventListener("submit", (e) => {
  // form要素としての元々の送信処理を停止
  e.preventDefault();
  // テキストを取得
  const text = textInput.value;
  // テキストも画像も空の場合は送信しない
  if(text == "" && imageBase64 == ""){
    return;
  }
  // テキストと画像(Base64)を送信
  ws.send(
    JSON.stringify({ "text": text, "image": imageBase64 })
  );
  // 入力フォームと画像情報を空にする
  textInput.value = "";
  imageInput.value = "";
  imageBase64 = "";
});
...

python/websocket(受け取ったデータをチャット参加者へ送信)

consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer

class ChatConsumer( AsyncWebsocketConsumer ):
  ...
  # JavaScriptから送られてきたデータを整理
  async def receive( self, text_data ):
    text_data_json = json.loads( text_data )
    ...
      text = text_data_json['text']
      image = text_data_json['image']
      data = {
        'type': 'chat_message',
        'text': text,
        'image' : image,
      }
      await self.channel_layer.group_send( self.room_name, data )

  # 整理したデータをチャット参加者のJavaScriptへ送る
  async def chat_message( self, data ):
    data_json = {
      'text': data['text'],
      'image': data['image'],
    }
    await self.send( text_data=json.dumps( data_json ) )
  ...

★JavaScript(データを受信して表示)

chat.js
...
const messages = document.getElementById("messages");

ws.onmessage = (event) => {
  // 送られてきたデータを変数に格納
  const data = JSON.parse(event.data);

  // チャット内容をまとめるdiv要素を作成
  const message = document.createElement('div');

  // 送られてきたテキストをp要素としてmessageに組み込む
  const messageText = document.createElement('p');
  messageText.innerText = data["message"];
  message.append(messageText);

  // 送られてきた画像をimg要素としてmessageに組み込む
  const messageImage = document.createElement('img');
  messageImage.setAttribute('src', data["image"]);
  message.append(messageImage);

  // チャット内容を表示させる
  messages.append(message);
};
...
  • Base64形式の画像は、その文字列をimg要素src属性に指定することで画像として表示する事ができる。

まとめ

画像URLimg要素canvas要素Base64img要素という複雑な流れを経て、画像を非同期通信で送る事ができた。

参考URL

[JavaScript] 画像変換:要素 ⇔ Base64(相互変換)

Webチャットアプリを作る ( Django + Channels )

2
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
5