Search code examples
djangodjango-channels

are there any clever ways to save files with Web socket in Django?


I have built chatting site and I'm thinking that I can add the function of sending files besides message. But it seems that web socket can't send files or images. My solution is that when file is sent, I use api and then fetched messages and files are reordered by timestamp. But if there are better ways to achieve this, I really want to know.

So, my question is

  1. Is it possible to send files by web socket and get them by WebsocketConsumer?
  2. Is there any ways to combine those two processes? (ex. you send provisional link of files and store them as FileField? )

model

from django.db import models
from django.contrib.auth import get_user_model

User = get_user_model()


class Contact(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='contact_user')
    friends = models.ManyToManyField('self', blank=True, related_name='friends')


    def __str__(self):
        return self.user.name

class Message(models.Model):
    contact = models.ForeignKey(Contact, related_name='message', on_delete=models.CASCADE)
    content = models.TextField()
    timestamp = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.contact.user.name

class File(models.Model):
    contact = models.ForeignKey(Contact, related_name='file', on_delete=models.CASCADE)
    file = models.FileField()
    timestamp = models.DateTimeField(auto_now_add=True)

class Chat(models.Model):
    participants = models.ManyToManyField(Contact, related_name='chats')
    messages = models.ManyToManyField(Message, blank=True)
    files = models.ManyToManyField(File, blank=True, null=True)
    timestamp = models.DateTimeField(auto_now_add=True)


    def last_30_messages(self):
        return self.messages.order_by('-timestamp').all()[:30]


    def __str__(self):
        return "{}".format(self.pk)

I don't know what information is needed to figure this out, so I paste only Model. If something is unclear, let me know.

Thank you :)


Solution

  • You can encode the image in base64 format in frontend and decode on the server side, I would suggest to compress the image in the browser if you don't care much about the image quality since it can reduce the image size to a large content.

    On the server side, you can read the decoded bytes as ContentFile so that it can work perfectly with Django ORM:

    from django.core.files.base import ContentFile
    import base64
    
    def base64_decode(data, name):
        '''decode the base64 string and create a compatible 
           file that Django recognize
        '''
        format, imgstr = data.split(';base64,') 
        ext = format.split('/')[-1]
        data = ContentFile(base64.b64decode(imgstr), name=name + '.' + ext)
        return data
    
    # save the file
    file = base64_decode(message, 'file_name')
    Yourmodel.objects.create(file=file, ...)