如何使用django rest框架发送文件作为响应?

Dhr*_*ruv 8 python api django rest django-rest-framework

我需要发送一个pdf文件和一些其他参数来响应使用django rest框架的get API调用.

我该怎么做?我尝试了这个,但它给出了一个错误<django.http.HttpResponse object at 0x7f7d32ffafd0> is not JSON serializable.

@detail_route(methods=['get'])
def fetch_report(self, request, *args, **kwargs):
    short_report = open("somePdfFile", 'rb')
    response = HttpResponse(FileWrapper(short_report), content_type='application/pdf')
    return Response({'detail': 'this works',
        'report': response})
Run Code Online (Sandbox Code Playgroud)

Kev*_*own 11

这里的问题是你试图返回JSON和PDF的混合,这可能不是你想要的,或者将返回一个巨大的 base64编码响应.PDF是二进制格式,JSON是一种文本格式,你不能真正地将它们混合起来.

在DRF视图中,您可以直接返回一个Django响应,您已经看到它正在生成(the HttpResponse),DRF将通过它并跳过呈现器.这在这种情况下很有用,因为它允许您返回二进制响应,如图像或PDF,而不必担心DRF的渲染层导致问题.

@detail_route(methods=['get'])
def fetch_report(self, request, *args, **kwargs):
    short_report = open("somePdfFile", 'rb')
    response = HttpResponse(FileWrapper(short_report), content_type='application/pdf')
    return response
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用类似base64编码的方式将PDF编码为文本.这将大大增加您的响应大小,但它将允许您毫无问题地使用DRF的渲染层.

@detail_route(methods=['get'])
def fetch_report(self, request, *args, **kwargs):
    import base64
    short_report = open("somePdfFile", 'rb')
    report_encoded = base64.b64encode(short_report.read())
    return Response({'detail': 'this works',
        'report': report_encoded})
Run Code Online (Sandbox Code Playgroud)

但我建议的路线是生成PDF并将其存储在您的媒体存储或其他私人位置,并在您的回复中提供直接链接.这样您就不必担心编码问题,不需要直接返回PDF,也不必担心直接提供PDF.


Eug*_*ash 6

如果您确实需要直接从后端提供二进制文件(例如,当它们是动态生成的时候),您可以使用自定义渲染器:

from rest_framework.renderers import BaseRenderer

class BinaryFileRenderer(BaseRenderer):
    media_type = 'application/octet-stream'
    format = None
    charset = None
    render_style = 'binary'

    def render(self, data, media_type=None, renderer_context=None):
        return data
Run Code Online (Sandbox Code Playgroud)

然后在视图集中的操作中使用它:

@detail_route(methods=['get'], renderer_classes=(BinaryFileRenderer,))
def download(self, request, *args, **kwargs):
    with open('/path/to/file.pdf', 'rb') as report:
        return Response(
            report.read(),
            headers={'Content-Disposition': 'attachment; filename="file.pdf"'},
            content_type='application/pdf')
Run Code Online (Sandbox Code Playgroud)


drg*_*986 2

您可以将DRF-PDF项目与PDFFileResponse一起使用:

from rest_framework import status
from rest_framework.views import APIView
from drf_pdf.response import PDFFileResponse
from drf_pdf.renderer import PDFRenderer


class PDFHandler(APIView):

    renderer_classes = (PDFRenderer, )

    def get(self, request):
        return PDFFileResponse(
            file_path='/path/to/file.pdf',
            status=status.HTTP_200_OK
        )
Run Code Online (Sandbox Code Playgroud)

但是,也许您无法以两种格式(json 和流)进行响应。