web-dev-qa-db-fra.com

Comment retourner le téléchargement du fichier généré avec Django REST Framework?

Je dois renvoyer le téléchargement du fichier généré sous la forme Django REST Réponse du framework. J'ai essayé ce qui suit:

def retrieve(self, request, *args, **kwargs):
    template = webodt.ODFTemplate('test.odt')
    queryset = Pupils.objects.get(id=kwargs['pk'])
    serializer = StudentSerializer(queryset)
    context = dict(serializer.data)
    document = template.render(Context(context))
    doc = converter().convert(document, format='doc')
    res = HttpResponse(
        FileWrapper(doc),
        content_type='application/msword'
    )
    res['Content-Disposition'] = u'attachment; filename="%s_%s.Zip"' % (context[u'surname'], context[u'name'])
    return res

Mais il renvoie un document msword sous la forme json.

Comment puis-je faire commencer le téléchargement en tant que fichier?

15
Viktor

J'ai résolu mon problème en enregistrant le fichier dans le dossier multimédia et en envoyant le lien vers le front-end.

@permission_classes((permissions.IsAdminUser,))
class StudentDocxViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    def retrieve(self, request, *args, **kwargs):
        template = webodt.ODFTemplate('test.odt')
        queryset = Pupils.objects.get(id=kwargs['pk'])
        serializer = StudentSerializer(queryset)
        context = dict(serializer.data)
        document = template.render(Context(context))
        doc = converter().convert(document, format='doc')
        p = u'docs/cards/%s/%s_%s.doc' % (datetime.now().date(), context[u'surname'], context[u'name'])
        path = default_storage.save(p, doc)
        return response.Response(u'/media/' + path)

Et géré cela comme dans mon front-end (AngularJS SPA)

$http(req).success(function (url) {
    console.log(url);
    window.location = url;
})
2
Viktor

Cela peut fonctionner pour vous:

file_path = file_url
FilePointer = open(file_path,"r")
response = HttpResponse(FilePointer,content_type='application/msword')
response['Content-Disposition'] = 'attachment; filename=NameOfFile'

return response.

Pour le code FrontEnd, reportez-vous à this

6
Piyush S. Wanare

Voici un exemple de renvoi d'un téléchargement de fichier directement depuis DRF. L'astuce consiste à utiliser un rendu personnalisé afin que vous puissiez retourner une réponse directement à partir de la vue:

from Django.http import FileResponse
from rest_framework import viewsets, renderers
from rest_framework.decorators import action

class PassthroughRenderer(renderers.BaseRenderer):
    """
        Return data as-is. View should supply a Response.
    """
    media_type = ''
    format = ''
    def render(self, data, accepted_media_type=None, renderer_context=None):
        return data

class ExampleViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Example.objects.all()

    @action(methods=['get'], detail=True, renderer_classes=(PassthroughRenderer,))
    def download(self, *args, **kwargs):
        instance = self.get_object()

        # get an open file handle (I'm just using a file attached to the model for this example):
        file_handle = instance.file.open()

        # send file
        response = FileResponse(file_handle, content_type='whatever')
        response['Content-Length'] = instance.file.size
        response['Content-Disposition'] = 'attachment; filename="%s"' % instance.file.name

        return response

Remarque J'utilise un point de terminaison personnalisé download au lieu du point de terminaison par défaut retrieve, car cela permet de remplacer facilement le rendu uniquement pour ce point de terminaison au lieu de l'ensemble de la vue - et cela tend à pour donner un sens à la liste et aux détails pour renvoyer le JSON normal de toute façon. Si vous souhaitez retourner sélectivement un téléchargement de fichier, vous pouvez ajouter plus de logique au rendu personnalisé.

5
Jack Cushman

J'ai simplifié l'ensemble du processus dans ma réponse à une autre question similaire. N'hésitez pas à vous référer à ma réponse. Voici le lien vers ma réponse: https://stackoverflow.com/a/61075310/9142137

Dans ma réponse, j'ai montré comment télécharger des images (fichier) comme réponse pour votre API au lieu de renvoyer le fichier sous json

0
Suraj S Jain

J'utilise DRF et j'ai trouvé un code de vue pour télécharger le fichier, qui serait comme

from rest_framework import generics
from Django.http import HttpResponse
from wsgiref.util import FileWrapper

class FileDownloadListAPIView(generics.ListAPIView):

    def get(self, request, id, format=None):
        queryset = Example.objects.get(id=id)
        file_handle = queryset.file.path
        document = open(file_handle, 'rb')
        response = HttpResponse(FileWrapper(document), content_type='application/msword')
        response['Content-Disposition'] = 'attachment; filename="%s"' % queryset.file.name
        return response

et url.py sera

path('download/<int:id>/',FileDownloadListAPIView.as_view())

J'utilise React.js en frontend et j'obtiens une réponse comme

handleDownload(id, filename) {
  fetch(`http://127.0.0.1:8000/example/download/${id}/`).then(
    response => {
      response.blob().then(blob => {
      let url = window.URL.createObjectURL(blob);
      let a = document.createElement("a");
      console.log(url);
      a.href = url;
      a.download = filename;
      a.click();
    });
  });
}

et après avoir réussi à télécharger un fichier qui s'ouvre également correctement et j'espère que cela fonctionnera. Merci

0
Muhammad Zohaib