Saving a blob to a file in python

18,576

Solution 1

The other solutions were helpful where it would write the file to the disk still it would say incorrect file format or unable to play the file because of missing plugins.

It was something to do with JavaScript (which I'm not much comfortable with) where I had to have all meta data in FormData I guess. I'm not sure why this works. Had searched somewhere and found this which worked.

Would be great to know what went wrong above. Would accept any other answer explaining this.

class SaveVideo(APIView):
    def post(self, request):
        filename = 'demo.mp4'
        with open(filename, 'wb+') as destination:
            for chunk in request.FILES['video-blob'].chunks():
                destination.write(chunk)
        return Response({"status":"ok"})

Javascript

function xhr(url, data, callback) {
      var request = new XMLHttpRequest();
            request.onreadystatechange = function () {
                if (request.readyState == 4 && request.status == 200) {
                   callback(request.responseText);
                }
            };

    request.open('POST', url);
    request.setRequestHeader('X-CSRFToken',Cookies.get('csrftoken'))
    request.send(data);
    }

    var fileType = 'video'; 


    var fileName = 'ABCDEF.webm'; 

    var formData = new FormData();
    formData.append(fileType , fileName);
    formData.append(fileType + '-blob', blob);
    xhr(url,formData,callback_function);

Solution 2

You could using Python's base64 library to encode and decode data in your SaveBlob class:

import base64

video_stream = "hello"

with open('file.webm', 'wb') as f_vid:
    f_vid.write(base64.b64encode(video_stream))

with open('file.webm', 'rb') as f_vid:
    video_stream = base64.b64decode(f_vid.read())

print video_stream

Giving you back the original video_stream:

hello

For this simple example, the saved file would appear as:

aGVsbG8=

Solution 3

The first argument to struct.pack is a format string that specifies the layout of the struct. You are only passing the bytes that you want to pack, so this is interpreted as an invalid format:

>>> bs = b'\x01\x56\x56'
>>> struct.pack(bs)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
struct.error: bad char in struct format

Constructing a valid format string fixes this (but note the you need to construct the format string based on your data and platform):

>> n = len(bs)    # 3
>>> fmt = '{:d}s'.format(n)    # '3s'
>>> struct.pack(fmt, bs)
b'\x01VV'

It's unlikely to be necessary to pack* or base64-encode the data if it's just being written to disk; just write the bytes to file directly:

class SaveBlob(APIView):
    def post(self, request):
        with open("file.webm", "wb") as vid:
            video_stream = request.FILES['blob'].read()
            vid.write(video_stream)
            return Response()

Your video player should be able to read the binary file and interpret it correctly.

Base64 encoding is really for transferring binary data when the transfer mechanism expects ascii-encoded data, so there's no benefit in applying this encoding just to write to a file. If you really need to base64-encode your data, use the python's base64 package as Martin Evans recommends in his answer..

* It may be necessary to pack the data if it's being moved between platforms with different endianness.

Share:
18,576
Akshay Hazari
Author by

Akshay Hazari

#SOreadytohelp I am nerdier than 93% of all people. Are you a nerd? Click here to take the Nerd Test, get nerdy images and jokes, and write on the nerd forum! http://www.nerdtests.com/images/ft/nq/3da57b0395.gif

Updated on June 14, 2022

Comments

  • Akshay Hazari
    Akshay Hazari almost 2 years

    I am trying to save a blob sent via ajax, as a file in python. Had been through this before Python: How do I convert from binary to base 64 and back?

    class SaveBlob(APIView):
        def post(self, request):
            vid = open("file.webm", "wb")
            video_stream = request.FILES['blob'].read()
            video_stream = struct.pack(video_stream).encode('base64')
            # vid.write(video_stream.decode('base64'))
            vid.write(video_stream)
            vid.close()
            return Response()
    

    It results in error: bad char in struct format

    Simply using this vid.write(video_stream.decode('base64')) without usingstruct.pack saves the file but when I open the video it results in could not determine type of stream.

    The ajax call goes like this but it looks fine I guess.

    function call_ajax(request_type,request_url,request_data) {
    
        var data_vid = new FormData();
        console.log(request_url);
        data_vid.append('blob', request_data);
        console.log(request_data);
    
        var data= [];
        try{
            $.ajax({
                type: request_type,
                url: request_url,
                data:data_vid,
                cors:true,
                processData: false,
                contentType: false,
                async:false,
                beforeSend: function(xhr) {
                        xhr.setRequestHeader('X-CSRFToken',Cookies.get('csrftoken'))
                 },
    
                success: function(response){
                    data =response;
                }
            });
        }catch(error){
            console.log(error);
        }
        return data;
    }
    

    Any help with it will be appreciated. Any suggestions about any flaws or possible causes are welcome.