【问题标题】:Django channels file/image uploadDjango 频道文件/图片上传
【发布时间】:2017-05-20 10:28:42
【问题描述】:

我想使用django-channels 上传文件和图像,但我不知道从哪里开始。似乎没有太多关于 websockets 和文件/图像上传的文档。有什么想法吗?

【问题讨论】:

  • 有什么特别的原因你不能用 AJAX 完成这个吗? Django 通道的一个问题是最大消息大小为 1MB,因此对于一个强大的解决方案,您还必须将文件分成块,然后在服务器端重新组合它们。
  • Ajax 或使用 3rd 方包就足够了。它们也是相当多的 django 插件,用于将图像直接上传到 s3。 github.com/sbc/django-uploadify-s3
  • 您找到解决方案了吗?
  • 是的,因为 django-channels 或更好的 daphne 服务器的最大包大小为 1mb,我已将文件放入 javascript 中并使用文件上传使用者上传。
  • @PyUnchained 我认为 Channels 没有最大消息大小。我认为它仅由 Redis 决定。 IMO 最好使用频道进行上传,因为您可以将其分块并显示进度条。

标签: websocket upload django-channels


【解决方案1】:

我也遇到了同样的问题,我通过上传 S3 存储桶中的图像/文件解决了这个问题。我们只需要解码base64代码并上传文件并将URL返回给websocket。我们还可以通过提供文件类型来提供图像的预览。

def file_upload(self, data):
    # Convert decode the base64 data 
    file = base64.b64decode(data['data']['content'].split(',')[-1])
    filename = data['data']['filename']
    type = data['data']['type']
    AWS_ACCESS_KEY_ID = getattr(settings, "AWS_S3_ACCESS_KEY_ID")
    AWS_SECRET_ACCESS_KEY = getattr(settings, "AWS_S3_SECRET_ACCESS_KEY")
    bucket_name = getattr(settings, "AWS_STORAGE_BUCKET_NAME")
    conn = boto.connect_s3(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
    bucket = conn.get_bucket(bucket_name)
    k = Key(bucket)
    k.key = getattr(settings, "AWS_CHAT_DIR") + '/' + filename
    k.set_metadata('Content-Type', type)
    k.set_contents_from_string(file)
    url = 'https://' + getattr(settings, "AWS_BUCKET_URL") + '/' + k.key
    message = url
    content = {
        'command': 'new_message',
        'message': self.message_to_json(message)
    }
    return self.send_chat_message(content)

【讨论】:

  • 这里您接受作为 file_upload 函数参数的数据是什么?你也可以发布你的那部分代码吗?
【解决方案2】:

唯一真正的方法是使用通道 2,因为消费者在会话期间持续存在,您可以将文件对象作为实例变量放在其上。

您可以看到我如何使用文件字段并首先在其上发送一些初始化参数。我抽出了重要的部分:

// Some class setup before this

// File field object
this.file = field.files[0];
init_params.action = 'prepare';
init_params.file_name = this.file.name;
init_params.file_size = this.file.size;

// This is a wrapper I have that gets a websocket. 
// The callback is called on connect. It sends the initialization parameters.
var ws = get_websocket(url, function(){
    this.send(JSON.stringify(init_params));
});

// After the initialization params are sent this is called with 0 as a parameter. 
// As the web server oks each chunk this is called with the next offset to send. chunk_size needs to be set somewhere.
var load_data = function(index) {
    var end =  index + this.chunk_size;
    if (index >= this.file.size)
        return;
    if (end > this.file.size)
        end = this.file.size;
    ws.send(this.file.slice(index, end));
}.bind(this);

ws.onmessage = function(msg) {
    var message = JSON.parse(msg.data);
    switch (message.action) {
        case 'progress':
            // This is called each time a chunk is written
            load_data(message.file_size);
            break;
        case 'ready':
            // This comes in to kick everything off
            load_data(0);
            break;
        case 'complete':
            // Handle complete
            break;
            }
        };
    }.bind(this);

在消费者中,我们检查它是否是文本数据,如果是,我们将其处理为 JSON 以设置文件。如果是二进制数据,我们就这样写出每个块。

    async def handle_json(self, message):
         self.session = {
             # Set up file object and attributes
         }

    async def handle_chunk(self, message, **kwargs):
        upload_size = self.session.get('upload_size')
        temp_destination = self.session.get('upload_file')

        if not upload_size or not temp_destination:
            return self.error('Invalid request. Please try again.')

        self.session['upload_file'].write(message)
        size = self.session['upload_file'].tell()

        percent = round((size / upload_size) * 100)
        await self.send_json({
            'action': 'progress',
            'percent': percent,
            'file_size': size
        })

        if size >= upload_size:
            self.session['upload_file'].flush()
            file_name = await self.handle_complete(self.session['upload_file'])

            await self.send_json({
                'action': 'complete',
                'file_size': size,
                'file_name': file_name
            }, close=True)

【讨论】:

    【解决方案3】:

    简单的方法是使用 FileReader。在客户端,您将拥有以下代码。

    //connect the websocket with server
    var client = new WebSocket('ws://hostname')
    
    //now get the base64 src for image for it i have 
      written a function.
    
    function toDataURL(file, callback) {
        var reader = new FileReader();
        reader.onload = function () {
            var dataURL = reader.result;
            callback(dataURL);
        }
        reader.readAsDataURL(file);
    }
    
    document.getElementById('image').addEventListener('change', function(event){
        var files = event.target
        var file = files[0];
        toDataURL(file, function(dataURL){
        client.send(JSON.stringify({
            'command':'new_file', 
            'dataURL': dataURL
        }))
        })
    })
    

    这会将 dataURL 发送给您的消费者,然后您可以从那里将其发送给组,即发送给所有用户,并且可以将此 dataURL 作为图像的 src 以在浏览器上显示图像:)

    例如:

    <img src=dataURL>
    

    您可以使用 javascript 函数创建它,例如您可以使用 document.createElement('img') 并使用 javascript 将 src 作为 dataURL 提供。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-02-05
      • 2014-03-20
      • 1970-01-01
      • 2023-02-10
      • 1970-01-01
      • 2010-11-07
      • 1970-01-01
      相关资源
      最近更新 更多