7

I'm trying to get Django's HttpResponse to return binary data without much success. I've been trying different methods for a while now, but without success.

Encoding the string to ASCII works as long as the binary data values aren't outside the ASCII char scope, which is smaller than 0-255. The same happened when encoding with latin-1.

Creating byte string works nicely, but seems to fail if certain values are included, for example, if I have the following bytes included in the data: "\xf6\x52", I will get different bytes as a result. For some reason, the first byte, \xf6, gets converted to 0xfffd when I'm trying to review the result response.

I'd love to get some feedback and help with this.

Many thanks!

-A-

user3144420
  • 83
  • 1
  • 1
  • 3

3 Answers3

12
return HttpResponse(data, content_type='application/octet-stream')

worked for me.

sadashiv30
  • 601
  • 2
  • 7
  • 15
  • 4
    Good answers *explain* as well as provide code. Consider updating your answer to include an explanation about how this code works and why it is the best option. – Ajean Sep 22 '15 at 00:13
  • The required import: 'from django.http import HttpResponse' – roeland Jul 20 '19 at 13:08
  • Just want to add that this helped me a lot while using a combination of Django, DRF, WeasyPrint, and drf-pdf for returning PDF files. I was going nuts with Unicode errors, and found that replacing all the nonsense I had with this one line made the thing work! Sure, `octet-stream` is probably not the right content type for my case, but at least the API is working and I just need to tweak a minor thing. :-) – ankush981 May 29 '21 at 14:48
  • I get `TypeError: Object of type HttpResponse is not JSON serializable` – Y4RD13 Dec 15 '21 at 02:31
4

A flexible way to return binary data from Django is to first encode the data using Base64.

Base64 is a group of similar binary-to-text encoding schemes that represent binary data in an ASCII string format by translating it into a radix-64 representation.

Since the Base64 encoded data is ASCII, the default HTTP response content type of text/html; charset=utf-8 works fine.

Image Example

Django

import base64
from io import BytesIO

from django.http import HttpRequest, HttpResponse
import PIL.Image


def image_test(request: HttpRequest) -> HttpResponse:
    file_stream = BytesIO()
    image_data = PIL.Image.open('/path/to/image.png')
    image_data.save(file_stream)
    file_stream.seek(0)
    base64_data = base64.b64encode(file_stream.getvalue()).decode('utf-8')
    return HttpResponse(base64_data)

Web Browser

After fetching the data from Django, the base64 data can be used to create a data URL.


// `data` fetched from Django as Base64 string
const dataURL = `data:image/png;base64,${data}`;
const newImage = new Image();
newImage.src = dataURL;
$('#ImageContainer').html(newImage);

JSON Response

The Base64 data may also be returned as part of a JSON response:

import base64
from io import BytesIO

from django.http import HttpRequest, JsonResponse
import PIL.Image

def image_test(request: HttpRequest) -> JsonResponse:
    file_stream = BytesIO()
    image_data = PIL.Image.open('/path/to/image.png')
    image_data.save(file_stream)
    file_stream.seek(0)
    base64_data = base64.b64encode(file_stream.getvalue()).decode('utf-8')
    json_data = dict()
    json_data['base64Data'] = base64_data
    return JsonResponse(json_data)
Christopher Peisert
  • 21,862
  • 3
  • 86
  • 117
1

Here is an example code how to response binary data (Excel file in my case) with Django:

def download_view(request):
    # If you want to respond local file 
    with open('path/to/file.xlsx', 'rb') as xl:
        binary_data = xl.read()

    # Or if you want to generate file inside program (let's, say it will be openpyxl library)
    wb = openpyxl.load_workbook('some_file.xlsx')
    binary_object = io.BytesIO()
    wb.save(binary_object)
    binary_object.seek(0)
    binary_data = binary_object.read()

    file_name = f'FILENAME_{datetime.datetime.now().strftime("%Y%m%d")}.xlsx'

    response = HttpResponse(
        # full list of content_types can be found here
        # https://stackoverflow.com/a/50860387/13946204
        content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        headers={'Content-Disposition': f'attachment; filename="{file_name}"'},
    )
    response.write(binary_data)
    return response
rzlvmp
  • 7,512
  • 5
  • 16
  • 45