JavaScript / jQuery pour télécharger un fichier via POST avec des données JSON

250

J'ai une webapp d'une seule page basée sur jquery. Il communique avec un service Web RESTful via des appels AJAX.

J'essaie d'accomplir ce qui suit:

  1. Soumettez un POST contenant des données JSON à une URL REST.
  2. Si la demande spécifie une réponse JSON, alors JSON est renvoyé.
  3. Si la demande spécifie une réponse PDF / XLS / etc, un fichier binaire téléchargeable est renvoyé.

J'ai 1 & 2 travaillant maintenant, et l'application client jquery affiche les données renvoyées dans la page Web en créant des éléments DOM basés sur les données JSON. J'ai également le n ° 3 qui fonctionne du point de vue du service Web, ce qui signifie qu'il créera et renverra un fichier binaire s'il reçoit les paramètres JSON corrects. Mais je ne suis pas sûr de la meilleure façon de traiter # 3 dans le code javascript du client.

Est-il possible de récupérer un fichier téléchargeable à partir d'un appel ajax comme celui-ci? Comment faire pour que le navigateur télécharge et enregistre le fichier?

$.ajax({
    type: "POST",
    url: "/services/test",
    contentType: "application/json",
    data: JSON.stringify({category: 42, sort: 3, type: "pdf"}),
    dataType: "json",
    success: function(json, status){
        if (status != "success") {
            log("Error loading data");
            return;
        }
        log("Data loaded!");
    },
    error: function(result, status, err) {
        log("Error loading data");
        return;
    }
});

Le serveur répond avec les en-têtes suivants:

Content-Disposition:attachment; filename=export-1282022272283.pdf
Content-Length:5120
Content-Type:application/pdf
Server:Jetty(6.1.11)

Une autre idée est de générer le PDF et de le stocker sur le serveur et de renvoyer le JSON qui inclut une URL vers le fichier. Ensuite, lancez un autre appel dans le gestionnaire de réussite ajax pour faire quelque chose comme ceci:

success: function(json,status) {
    window.location.href = json.url;
}

Mais cela signifie que je devrais faire plusieurs appels au serveur, et mon serveur devrait créer des fichiers téléchargeables, les stocker quelque part, puis nettoyer périodiquement cette zone de stockage.

Il doit y avoir un moyen plus simple d'accomplir cela. Des idées?


EDIT: Après avoir examiné les documents pour $ .ajax, je vois que la réponse dataType ne peut être que l'un xml, html, script, json, jsonp, text, donc je suppose qu'il n'y a aucun moyen de télécharger directement un fichier à l'aide d'une demande ajax, à moins que j'incorpore le fichier binaire en utilisant Schéma d'URI de données comme suggéré dans la réponse @VinayC (ce qui n'est pas quelque chose que je veux faire).

Je suppose donc que mes options sont les suivantes:

  1. N'utilisez pas ajax et soumettez plutôt un post de formulaire et intégrez mes données JSON dans les valeurs du formulaire. Aurait probablement besoin de jouer avec des iframes cachés et autres.

  2. N'utilisez pas ajax et convertissez plutôt mes données JSON en une chaîne de requête pour créer une demande GET standard et définissez window.location.href sur cette URL. Peut-être besoin d'utiliser event.preventDefault () dans mon gestionnaire de clics pour empêcher le navigateur de changer à partir de l'URL de l'application.

  3. Utilisez mon autre idée ci-dessus, mais améliorée avec des suggestions de la réponse @naikus. Soumettez la demande AJAX avec un paramètre qui permet au service Web de savoir que cela est appelé via un appel ajax. Si le service Web est appelé à partir d'un appel ajax, renvoyez simplement JSON avec une URL vers la ressource générée. Si la ressource est appelée directement, renvoyez le fichier binaire réel.

Plus j'y pense, plus j'aime la dernière option. De cette façon, je peux récupérer des informations sur la demande (temps de génération, taille du fichier, messages d'erreur, etc.) et je peux agir sur ces informations avant de démarrer le téléchargement. L'inconvénient est une gestion supplémentaire des fichiers sur le serveur.

D'autres moyens d'y parvenir? Y a-t-il des avantages / inconvénients à ces méthodes que je devrais connaître?

Tauren
la source
possibilité de doublon du téléchargement du fichier Handle depuis ajax post
IsmailS
6
celui-ci était "un peu" plus tôt, alors comment est-ce un doublon
GiM
1
url = 'http://localhost/file.php?file='+ $('input').val(); window.open(url); La manière simple d'obtenir les mêmes résultats. Mettez l'en-tête dans le fichier php. Pas besoin d'envoyer un ajax ou d'obtenir des demandes
abhishek bagul

Réponses:

171

La solution de letronje ne fonctionne que pour des pages très simples. document.body.innerHTML +=prend le texte HTML du corps, ajoute l'iframe HTML et définit le innerHTML de la page sur cette chaîne. Cela supprimera, entre autres, toutes les liaisons d'événements de votre page. Créez un élément et utilisez-le à la appendChildplace.

$.post('/create_binary_file.php', postData, function(retData) {
  var iframe = document.createElement("iframe");
  iframe.setAttribute("src", retData.url);
  iframe.setAttribute("style", "display: none");
  document.body.appendChild(iframe);
}); 

Ou en utilisant jQuery

$.post('/create_binary_file.php', postData, function(retData) {
  $("body").append("<iframe src='" + retData.url+ "' style='display: none;' ></iframe>");
}); 

Ce que cela fait réellement: effectuer une publication dans /create_binary_file.php avec les données dans la variable postData; si cette publication se termine avec succès, ajoutez une nouvelle iframe au corps de la page. L'hypothèse est que la réponse de /create_binary_file.php inclura une valeur 'url', qui est l'URL à partir de laquelle le fichier PDF / XLS / etc généré peut être téléchargé. L'ajout d'un iframe à la page qui fait référence à cette URL entraînera le navigateur pour promouvoir l'utilisateur à télécharger le fichier, en supposant que le serveur Web a la configuration de type MIME appropriée.

SamStephens
la source
1
Je suis d'accord, c'est mieux que d'utiliser +=et je mettrai à jour mon application en conséquence. Merci!
Tauren
3
J'aime ce concept, mais Chrome a ce message dans la console: Resource interpreted as Document but transferred with MIME type application/pdf. Ensuite, il m'avertit également que le fichier peut être dangereux. Si je visite le retData.url directement, aucun avertissement ou problème.
Michael Irey
En regardant sur Internet, il semble que vous puissiez rencontrer des problèmes avec les PDF dans les iFrames si votre serveur ne définit pas correctement Content-Type et Content-Disposition. Je n'ai pas utilisé cette solution moi-même, je viens de clarifier une solution précédente, donc je n'ai pas d'autres conseils, je le crains.
SamStephens
1
@Tauren: Si vous acceptez qu'il s'agit d'une meilleure réponse, notez que vous pouvez changer la réponse acceptée à tout moment - ou même supprimer complètement la marque d'acceptation. Cochez simplement la nouvelle réponse et la marque d'acceptation sera transférée. (Vieille question, mais elle a été
signalée
5
quel est le retData.url supposé être?
Mark Thien
48

J'ai joué avec une autre option qui utilise des blobs. J'ai réussi à l'obtenir pour télécharger des documents texte, et j'ai téléchargé des PDF (cependant ils sont corrompus).

En utilisant l'API blob, vous pourrez effectuer les opérations suivantes:

$.post(/*...*/,function (result)
{
    var blob=new Blob([result]);
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="myFileName.txt";
    link.click();

});

Il s'agit d'IE 10+, Chrome 8+, FF 4+. Voir https://developer.mozilla.org/en-US/docs/Web/API/URL.createObjectURL

Il télécharge uniquement le fichier dans Chrome, Firefox et Opera. Cela utilise un attribut de téléchargement sur la balise d'ancrage pour forcer le navigateur à le télécharger.

JoshBerke
la source
1
Aucun support pour l'attribut de téléchargement sur IE et Safari :( ( caniuse.com/#feat=download ). Vous pouvez ouvrir une nouvelle fenêtre et y mettre du contenu, mais vous n'êtes pas sûr d'un PDF ou Excel ...
Marçal Juan
1
Vous devriez libérer de la mémoire du navigateur après avoir appelé click:window.URL.revokeObjectURL(link.href);
Edward Brey
@JoshBerke C'est vieux mais ça marche de mon côté. Comment puis-je faire en sorte que mon répertoire s'ouvre immédiatement pour être sauvegardé plutôt que d'être sauvegardé dans mon navigateur? Existe-t-il également un moyen de déterminer le nom du fichier entrant?
Jo Ko
2
Il corrompra les fichiers binaires, car ils sont convertis en chaînes en utilisant l'encodage, et le résultat n'est pas très proche du binaire d'origine ...
Gobol
2
Peut utiliser des mimetypes pour ne pas corrompre les informations utilisées ici avec les PDF: alexhadik.com/blog/2016/7/7/l8ztp8kr5lbctf5qns4l8t3646npqh
Mateo Tibaquira
18

Je connais ce genre de vieux, mais je pense avoir trouvé une solution plus élégante. J'ai eu exactement le même problème. Le problème que je rencontrais avec les solutions suggérées était qu'elles nécessitaient toutes l'enregistrement du fichier sur le serveur, mais je ne voulais pas enregistrer les fichiers sur le serveur, car cela introduisait d'autres problèmes (sécurité: le fichier était alors accessible par utilisateurs non authentifiés, nettoyage: comment et quand vous débarrasser des fichiers). Et comme vous, mes données étaient des objets JSON imbriqués complexes qui seraient difficiles à mettre dans un formulaire.

J'ai créé deux fonctions serveur. Le premier a validé les données. S'il y avait une erreur, elle serait retournée. Si ce n'était pas une erreur, j'ai renvoyé tous les paramètres sérialisés / encodés sous forme de chaîne base64. Ensuite, sur le client, j'ai un formulaire qui n'a qu'une seule entrée cachée et publie sur une deuxième fonction serveur. J'ai défini l'entrée masquée sur la chaîne base64 et soumis le format. La deuxième fonction serveur décode / désérialise les paramètres et génère le fichier. Le formulaire pourrait être soumis à une nouvelle fenêtre ou à un iframe sur la page et le fichier s'ouvrira.

Il y a un peu plus de travail et peut-être un peu plus de traitement, mais dans l'ensemble, je me sentais beaucoup mieux avec cette solution.

Le code est en C # / MVC

    public JsonResult Validate(int reportId, string format, ReportParamModel[] parameters)
    {
        // TODO: do validation

        if (valid)
        {
            GenerateParams generateParams = new GenerateParams(reportId, format, parameters);

            string data = new EntityBase64Converter<GenerateParams>().ToBase64(generateParams);

            return Json(new { State = "Success", Data = data });
        }

        return Json(new { State = "Error", Data = "Error message" });
    }

    public ActionResult Generate(string data)
    {
        GenerateParams generateParams = new EntityBase64Converter<GenerateParams>().ToEntity(data);

        // TODO: Generate file

        return File(bytes, mimeType);
    }

sur le client

    function generate(reportId, format, parameters)
    {
        var data = {
            reportId: reportId,
            format: format,
            params: params
        };

        $.ajax(
        {
            url: "/Validate",
            type: 'POST',
            data: JSON.stringify(data),
            dataType: 'json',
            contentType: 'application/json; charset=utf-8',
            success: generateComplete
        });
    }

    function generateComplete(result)
    {
        if (result.State == "Success")
        {
            // this could/should already be set in the HTML
            formGenerate.action = "/Generate";
            formGenerate.target = iframeFile;

            hidData = result.Data;
            formGenerate.submit();
        }
        else
            // TODO: display error messages
    }
amersk
la source
1
Je n'ai pas bien examiné cette solution, mais il convient de noter que rien sur la solution utilisant create_binary_file.php ne nécessite que les fichiers soient enregistrés sur le disque. Il serait tout à fait possible que create_binary_file.php génère des fichiers binaires en mémoire.
SamStephens
11

Il existe un moyen plus simple de créer un formulaire et de le publier, cela risque de réinitialiser la page si le type de mime de retour est quelque chose qu'un navigateur ouvrirait, mais pour csv et tel, c'est parfait

L'exemple nécessite un soulignement et jquery

var postData = {
    filename:filename,
    filecontent:filecontent
};
var fakeFormHtmlFragment = "<form style='display: none;' method='POST' action='"+SAVEAS_PHP_MODE_URL+"'>";
_.each(postData, function(postValue, postKey){
    var escapedKey = postKey.replace("\\", "\\\\").replace("'", "\'");
    var escapedValue = postValue.replace("\\", "\\\\").replace("'", "\'");
    fakeFormHtmlFragment += "<input type='hidden' name='"+escapedKey+"' value='"+escapedValue+"'>";
});
fakeFormHtmlFragment += "</form>";
$fakeFormDom = $(fakeFormHtmlFragment);
$("body").append($fakeFormDom);
$fakeFormDom.submit();

Pour des choses comme html, texte et autres, assurez-vous que le mimetype est quelque chose comme application / octet-stream

code php

<?php
/**
 * get HTTP POST variable which is a string ?foo=bar
 * @param string $param
 * @param bool $required
 * @return string
 */
function getHTTPPostString ($param, $required = false) {
    if(!isset($_POST[$param])) {
        if($required) {
            echo "required POST param '$param' missing";
            exit 1;
        } else {
            return "";
        }
    }
    return trim($_POST[$param]);
}

$filename = getHTTPPostString("filename", true);
$filecontent = getHTTPPostString("filecontent", true);

header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$filename\"");
echo $filecontent;
aqm
la source
8

Bref, il n'y a pas de moyen plus simple. Vous devez faire une autre demande de serveur pour afficher le fichier PDF. Cependant, il existe peu d'alternatives mais elles ne sont pas parfaites et ne fonctionneront pas sur tous les navigateurs:

  1. Regardez le schéma d'URI de données . Si les données binaires sont petites, vous pouvez peut-être utiliser javascript pour ouvrir la fenêtre transmettant les données dans l'URI.
  2. La seule solution pour Windows / IE serait d'avoir un contrôle .NET ou FileSystemObject pour enregistrer les données sur le système de fichiers local et les ouvrir à partir de là.
VinayC
la source
Merci, je n'étais pas au courant du schéma d'URI de données. Il semble que ce soit la seule façon de le faire en une seule demande. Mais ce n'est pas vraiment la direction que je veux aller.
Tauren
En raison des exigences du client, j'ai fini par résoudre ce problème en utilisant les en-têtes de serveur dans la réponse ( Content-Disposition: attachment;filename="file.txt"), mais je veux vraiment essayer cette approche la prochaine fois. J'avais déjà utilisé des URIdonnées pour des miniatures, mais je n'ai jamais pensé à les utiliser pour les téléchargements - merci de partager ce nugget.
brichins du
8

Cela fait un moment que cette question n'a pas été posée mais j'ai eu le même défi et je veux partager ma solution. Il utilise des éléments des autres réponses mais je n'ai pas pu le trouver dans son intégralité. Il n'utilise pas de formulaire ou d'iframe mais nécessite une paire de requêtes post / get. Au lieu d'enregistrer le fichier entre les demandes, il enregistre les données de publication. Cela semble à la fois simple et efficace.

client

var apples = new Array(); 
// construct data - replace with your own
$.ajax({
   type: "POST",
   url: '/Home/Download',
   data: JSON.stringify(apples),
   contentType: "application/json",
   dataType: "text",

   success: function (data) {
      var url = '/Home/Download?id=' + data;
      window.location = url;
   });
});

serveur

[HttpPost]
// called first
public ActionResult Download(Apple[] apples)
{
   string json = new JavaScriptSerializer().Serialize(apples);
   string id = Guid.NewGuid().ToString();
   string path = Server.MapPath(string.Format("~/temp/{0}.json", id));
   System.IO.File.WriteAllText(path, json);

   return Content(id);
}

// called next
public ActionResult Download(string id)
{
   string path = Server.MapPath(string.Format("~/temp/{0}.json", id));
   string json = System.IO.File.ReadAllText(path);
   System.IO.File.Delete(path);
   Apple[] apples = new JavaScriptSerializer().Deserialize<Apple[]>(json);

   // work with apples to build your file in memory
   byte[] file = createPdf(apples); 

   Response.AddHeader("Content-Disposition", "attachment; filename=juicy.pdf");
   return File(file, "application/pdf");
}
Frank Rem
la source
1
Je pense que je préfère cette solution. Dans mon cas, bien que je génère le fichier, je pense que j'essaierai de conserver le fichier en mémoire jusqu'à ce qu'il soit accessible, puis de libérer la mémoire après son téléchargement afin de ne jamais avoir à écrire sur le disque.
BVernon
5
$scope.downloadSearchAsCSV = function(httpOptions) {
  var httpOptions = _.extend({
    method: 'POST',
    url:    '',
    data:   null
  }, httpOptions);
  $http(httpOptions).then(function(response) {
    if( response.status >= 400 ) {
      alert(response.status + " - Server Error \nUnable to download CSV from POST\n" + JSON.stringify(httpOptions.data));
    } else {
      $scope.downloadResponseAsCSVFile(response)
    }
  })
};
/**
 * @source: https://github.com/asafdav/ng-csv/blob/master/src/ng-csv/directives/ng-csv.js
 * @param response
 */
$scope.downloadResponseAsCSVFile = function(response) {
  var charset = "utf-8";
  var filename = "search_results.csv";
  var blob = new Blob([response.data], {
    type: "text/csv;charset="+ charset + ";"
  });

  if (window.navigator.msSaveOrOpenBlob) {
    navigator.msSaveBlob(blob, filename); // @untested
  } else {
    var downloadContainer = angular.element('<div data-tap-disabled="true"><a></a></div>');
    var downloadLink      = angular.element(downloadContainer.children()[0]);
    downloadLink.attr('href', window.URL.createObjectURL(blob));
    downloadLink.attr('download', "search_results.csv");
    downloadLink.attr('target', '_blank');

    $document.find('body').append(downloadContainer);

    $timeout(function() {
      downloadLink[0].click();
      downloadLink.remove();
    }, null);
  }

  //// Gets blocked by Chrome popup-blocker
  //var csv_window = window.open("","","");
  //csv_window.document.write('<meta name="content-type" content="text/csv">');
  //csv_window.document.write('<meta name="content-disposition" content="attachment;  filename=data.csv">  ');
  //csv_window.document.write(response.data);
};
James McGuigan
la source
3

Pas entièrement une réponse au message d'origine, mais une solution rapide et sale pour publier un objet json sur le serveur et générer dynamiquement un téléchargement.

Côté client jQuery:

var download = function(resource, payload) {
     $("#downloadFormPoster").remove();
     $("<div id='downloadFormPoster' style='display: none;'><iframe name='downloadFormPosterIframe'></iframe></div>").appendTo('body');
     $("<form action='" + resource + "' target='downloadFormPosterIframe' method='post'>" +
      "<input type='hidden' name='jsonstring' value='" + JSON.stringify(payload) + "'/>" +
      "</form>")
      .appendTo("#downloadFormPoster")
      .submit();
}

.. puis décodage de la chaîne json sur le côté serveur et définition des en-têtes à télécharger (exemple PHP):

$request = json_decode($_POST['jsonstring']), true);
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename=export.csv');
header('Pragma: no-cache');
ralftar
la source
J'aime cette solution. Dans la question du PO, il semble que le demandeur sache s'il doit s'attendre à un téléchargement de fichier ou à des données JSON, afin qu'il puisse décider du côté client et publier sur une URL différente, plutôt que de décider du côté serveur.
Sam
2

Je pense que la meilleure approche est d'utiliser une combinaison, votre deuxième approche semble être une solution élégante où les navigateurs sont impliqués.

Donc, selon la façon dont l'appel est effectué. (que ce soit un navigateur ou un appel de service Web), vous pouvez utiliser une combinaison des deux, avec l'envoi d'une URL au navigateur et l'envoi de données brutes à tout autre client de service Web.

naikus
la source
Ma deuxième approche me semble désormais plus attrayante. Merci d'avoir confirmé que c'est une solution valable. Suggérez-vous que je passe une valeur supplémentaire dans l'objet json qui indique que cette demande est effectuée à partir d'un navigateur en tant qu'appel ajax au lieu d'un appel de service Web? Il y a plusieurs façons d'y parvenir que je peux penser, mais quelle technique utiliseriez-vous?
Tauren
cela ne peut-il pas être déterminé par l'en-tête http User-Agent? Ou tout autre en-tête http?
naikus
1

Je suis réveillé depuis deux jours maintenant pour essayer de comprendre comment télécharger un fichier en utilisant jquery avec un appel ajax. Tout le soutien que j'ai obtenu n'a pas pu aider ma situation jusqu'à ce que j'essaye.

Côté client

function exportStaffCSV(t) {
   
    var postData = { checkOne: t };
    $.ajax({
        type: "POST",
        url: "/Admin/Staff/exportStaffAsCSV",
        data: postData,
        success: function (data) {
            SuccessMessage("file download will start in few second..");
            var url = '/Admin/Staff/DownloadCSV?data=' + data;
            window.location = url;
        },
       
        traditional: true,
        error: function (xhr, status, p3, p4) {
            var err = "Error " + " " + status + " " + p3 + " " + p4;
            if (xhr.responseText && xhr.responseText[0] == "{")
                err = JSON.parse(xhr.responseText).Message;
            ErrorMessage(err);
        }
    });

}

Du côté serveur

 [HttpPost]
    public string exportStaffAsCSV(IEnumerable<string> checkOne)
    {
        StringWriter sw = new StringWriter();
        try
        {
            var data = _db.staffInfoes.Where(t => checkOne.Contains(t.staffID)).ToList();
            sw.WriteLine("\"First Name\",\"Last Name\",\"Other Name\",\"Phone Number\",\"Email Address\",\"Contact Address\",\"Date of Joining\"");
            foreach (var item in data)
            {
                sw.WriteLine(string.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\"",
                    item.firstName,
                    item.lastName,
                    item.otherName,
                    item.phone,
                    item.email,
                    item.contact_Address,
                    item.doj
                    ));
            }
        }
        catch (Exception e)
        {

        }
        return sw.ToString();

    }

    //On ajax success request, it will be redirected to this method as a Get verb request with the returned date(string)
    public FileContentResult DownloadCSV(string data)
    {
        return File(new System.Text.UTF8Encoding().GetBytes(data), System.Net.Mime.MediaTypeNames.Application.Octet, filename);
        //this method will now return the file for download or open.
    }

Bonne chance.

Otis-iDev
la source
2
Cela fonctionnera, pour les petits fichiers. Mais si vous avez un gros fichier, vous allez rencontrer le problème de l'url trop longue. Essayez-le avec 1000 enregistrements, il cassera et n'exportera qu'une partie des données.
Michael Merrell
1

Je l'ai trouvé quelque part il y a longtemps et cela fonctionne parfaitement!

let payload = {
  key: "val",
  key2: "val2"
};

let url = "path/to/api.php";
let form = $('<form>', {'method': 'POST', 'action': url}).hide();
$.each(payload, (k, v) => form.append($('<input>', {'type': 'hidden', 'name': k, 'value': v})) );
$('body').append(form);
form.submit();
form.remove();
Den Nikitin
la source
0

Une autre approche au lieu d'enregistrer le fichier sur le serveur et de le récupérer, consiste à utiliser .NET 4.0+ ObjectCache avec une courte expiration jusqu'à la deuxième action (date à laquelle il peut être définitivement vidé). La raison pour laquelle je veux utiliser JQuery Ajax pour faire l'appel, c'est qu'il est asynchrone. La construction de mon fichier PDF dynamique prend un peu de temps et j'affiche une boîte de dialogue de spinner occupée pendant ce temps (cela permet également de faire d'autres travaux). L'approche consistant à utiliser les données renvoyées dans le "succès:" pour créer un blob ne fonctionne pas de manière fiable. Cela dépend du contenu du fichier PDF. Il est facilement corrompu par les données de la réponse, s'il n'est pas complètement textuel, c'est tout ce que Ajax peut gérer.

Wray Smallwood
la source
0

Solution

La pièce jointe Content-Disposition semble fonctionner pour moi:

self.set_header("Content-Type", "application/json")
self.set_header("Content-Disposition", 'attachment; filename=learned_data.json')

solution de contournement

application / octet-stream

Il m'arrivait quelque chose de similaire avec un JSON, pour moi côté serveur, je mettais l'en-tête sur self.set_header ("Content-Type", " application / json ") mais quand je l'ai changé en:

self.set_header("Content-Type", "application/octet-stream")

Il l'a automatiquement téléchargé.

Sachez également que pour que le fichier conserve le suffixe .json, vous en aurez besoin dans l'en-tête du nom de fichier:

self.set_header("Content-Disposition", 'filename=learned_data.json')
Mr-Programmes
la source
-1

Avec HTML5, vous pouvez simplement créer une ancre et cliquer dessus. Il n'est pas nécessaire de l'ajouter au document en tant qu'enfant.

const a = document.createElement('a');
a.download = '';
a.href = urlForPdfFile;
a.click();

Terminé.

Si vous voulez avoir un nom spécial pour le téléchargement, passez-le simplement dans l' downloadattribut:

const a = document.createElement('a');
a.download = 'my-special-name.pdf';
a.href = urlForPdfFile;
a.click();
réécrit
la source
4
la question concerne la méthode POST
dovid