javascript - servlet - php ajax download file




Télécharger un fichier par jQuery.Ajax (10)

Ajouter plus de choses à ci-dessus répondre pour télécharger un fichier

Ci-dessous est un code java spring qui génère byte Array

@RequestMapping(value = "/downloadReport", method = { RequestMethod.POST })
    public ResponseEntity<byte[]> downloadReport(
            @RequestBody final SomeObejct obj, HttpServletResponse response) throws Exception {

        OutputStream out = new ByteArrayOutputStream();
        // write something to output stream
        HttpHeaders respHeaders = new HttpHeaders();
        respHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        respHeaders.add("X-File-Name", name);
        ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
        return new ResponseEntity<byte[]>(bos.toByteArray(), respHeaders, HttpStatus.CREATED);
    }

Maintenant, dans le code javascript en utilisant FileSaver.js, vous pouvez télécharger un fichier avec le code ci-dessous

var json=angular.toJson("somejsobject");
var url=apiEndPoint+'some url';
var xhr = new XMLHttpRequest();
//headers('X-File-Name')
xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 201) {
        var res = this.response;
        var fileName=this.getResponseHeader('X-File-Name');
        var data = new Blob([res]);
        saveAs(data, fileName); //this from FileSaver.js
    }
}    
xhr.open('POST', url);
xhr.setRequestHeader('Authorization','Bearer ' + token);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.responseType = 'arraybuffer';
xhr.send(json);

Le ci-dessus va télécharger le fichier

J'ai une action Struts2 côté serveur pour le téléchargement de fichiers.

<action name="download" class="com.xxx.DownAction">
    <result name="success" type="stream">
        <param name="contentType">text/plain</param>
        <param name="inputName">imageStream</param>
        <param name="contentDisposition">attachment;filename={fileName}</param>
        <param name="bufferSize">1024</param>
    </result>
</action>

Cependant, quand j'appelle l'action en utilisant le jQuery:

$.post(
  "/download.action",{
    para1:value1,
    para2:value2
    ....
  },function(data){
      console.info(data);
   }
);

dans Firebug, je vois que les données sont récupérées avec le flux binaire . Je me demande comment ouvrir la fenêtre de téléchargement de fichiers avec laquelle l'utilisateur peut enregistrer le fichier localement?


Dans Rails, je le fais de cette façon:

function download_file(file_id) {
  let url       = '/files/' + file_id + '/download_file';
    $.ajax({
    type: 'GET',
    url: url,
    processData: false,
    success: function (data) {
       window.location = url;
    },
    error: function (xhr) {
     console.log(' Error:  >>>> ' + JSON.stringify(xhr));
    }
   });
 }

L'astuce est la partie window.location . La méthode du contrôleur ressemble à:

# GET /files/{:id}/download_file/
def download_file
    send_file(@file.file,
          :disposition => 'attachment',
          :url_based_filename => false)
end

J'ai fait face au même problème et l'ai résolu avec succès. Mon cas d'utilisation est celui-ci.

" Publier les données JSON sur le serveur et recevoir un fichier Excel Ce fichier Excel est créé par le serveur et renvoyé en réponse au client Télécharger cette réponse sous forme de fichier avec un nom personnalisé dans le navigateur "

$("#my-button").on("click", function(){

// Data to post
data = {
    ids: [1, 2, 3, 4, 5]
};

// Use XMLHttpRequest instead of Jquery $ajax
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    var a;
    if (xhttp.readyState === 4 && xhttp.status === 200) {
        // Trick for making downloadable link
        a = document.createElement('a');
        a.href = window.URL.createObjectURL(xhttp.response);
        // Give filename you wish to download
        a.download = "test-file.xls";
        a.style.display = 'none';
        document.body.appendChild(a);
        a.click();
    }
};
// Post data to URL which handles post request
xhttp.open("POST", excelDownloadUrl);
xhttp.setRequestHeader("Content-Type", "application/json");
// You should set responseType as blob for binary responses
xhttp.responseType = 'blob';
xhttp.send(JSON.stringify(data));
});

L'extrait ci-dessus ne fait que suivre

  • Publication d'un tableau en tant que JSON sur le serveur à l'aide de XMLHttpRequest.
  • Après avoir récupéré le contenu sous la forme d'un blob (binaire), nous créons une URL téléchargeable et l'attachons à un lien "a" invisible puis en cliquant dessus. J'ai fait une demande POST ici. Au lieu de cela, vous pouvez aller pour un simple GET aussi. Nous ne pouvons pas télécharger le fichier via Ajax, nous devons utiliser XMLHttpRequest.

Ici, nous devons soigneusement mettre quelques choses sur le serveur. J'ai mis peu d'en-têtes dans Python Django HttpResponse. Vous devez les définir en conséquence si vous utilisez d'autres langages de programmation.

# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")

Depuis que j'ai téléchargé xls (excel) ici, j'ai ajusté contentType à plus d'un. Vous devez le définir en fonction de votre type de fichier. Vous pouvez utiliser cette technique pour télécharger n'importe quel type de fichiers.


J'ai trouvé un correctif qui, bien qu'il n'utilise pas réellement ajax, vous permet d'utiliser un appel javascript pour demander le téléchargement et obtenir un rappel lorsque le téléchargement commence réellement. J'ai trouvé cela utile si le lien exécute un script côté serveur qui prend un peu de temps pour composer le fichier avant de l'envoyer. donc vous pouvez les alerter que c'est le traitement, puis quand il envoie finalement le fichier supprimer cette notification de traitement. C'est pourquoi je voulais d'abord charger le fichier via ajax afin que je puisse avoir un événement quand le fichier est demandé et un autre lorsqu'il commence à télécharger.

le js sur la première page

function expdone()
{
    document.getElementById('exportdiv').style.display='none';
}
function expgo()
{
   document.getElementById('exportdiv').style.display='block';
   document.getElementById('exportif').src='test2.php?arguments=data';
}

l'iframe

<div id="exportdiv" style="display:none;">
<img src="loader.gif"><br><h1>Generating Report</h1>
<iframe id="exportif" src="" style="width: 1px;height: 1px; border:0px;"></iframe>
</div>

puis l'autre fichier:

<!DOCTYPE html>
<html>
<head>
<script>
function expdone()
{
    window.parent.expdone();
}
</script>
</head>
<body>
<iframe id="exportif" src="<?php echo "http://10.192.37.211/npdtracker/exportthismonth.php?arguments=".$_GET["arguments"]; ?>"></iframe>
<script>document.getElementById('exportif').onload= expdone;</script>
</body></html>

Je pense qu'il y a un moyen de lire obtenir des données en utilisant js alors aucun php ne serait nécessaire. mais je ne le connais pas de la main et le serveur que j'utilise supporte PHP donc cela fonctionne pour moi. J'ai pensé que je le partagerais au cas où ça aiderait quelqu'un.


Ok donc voici le code de travail lors de l'utilisation de MVC et vous obtenez votre fichier à partir d'un contrôleur

disons que vous avez votre tableau d'octets déclarent et peuplent, la seule chose que vous devez faire est d'utiliser la fonction Fichier (en utilisant System.Web.Mvc)

byte[] bytes = .... insert your bytes in the array
return File(bytes, System.Net.Mime.MediaTypeNames.Application.Octet, "nameoffile.exe");

puis, dans le même contrôleur, ajoutez ces deux fonctions

protected override void OnResultExecuting(ResultExecutingContext context)
    {
        CheckAndHandleFileResult(context);

        base.OnResultExecuting(context);
    }

    private const string FILE_DOWNLOAD_COOKIE_NAME = "fileDownload";

    /// <summary>
    /// If the current response is a FileResult (an MVC base class for files) then write a
    /// cookie to inform jquery.fileDownload that a successful file download has occured
    /// </summary>
    /// <param name="context"></param>
    private void CheckAndHandleFileResult(ResultExecutingContext context)
    {
        if (context.Result is FileResult)
            //jquery.fileDownload uses this cookie to determine that a file download has completed successfully
            Response.SetCookie(new HttpCookie(FILE_DOWNLOAD_COOKIE_NAME, "true") { Path = "/" });
        else
            //ensure that the cookie is removed in case someone did a file download without using jquery.fileDownload
            if (Request.Cookies[FILE_DOWNLOAD_COOKIE_NAME] != null)
                Response.Cookies[FILE_DOWNLOAD_COOKIE_NAME].Expires = DateTime.Now.AddYears(-1);
    }

et alors vous serez en mesure d'appeler votre contrôleur pour télécharger et obtenir le rappel "succès" ou "échec"

$.fileDownload(mvcUrl('name of the controller'), {
            httpMethod: 'POST',
            successCallback: function (url) {
            //insert success code

            },
            failCallback: function (html, url) {
            //insert fail code
            }
        });

Ok, basé sur le code de ndpu, voici une version améliorée (je pense) d'ajax_download;

function ajax_download(url, data) {
    var $iframe,
        iframe_doc,
        iframe_html;

    if (($iframe = $('#download_iframe')).length === 0) {
        $iframe = $("<iframe id='download_iframe'" +
                    " style='display: none' src='about:blank'></iframe>"
                   ).appendTo("body");
    }

    iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument;
    if (iframe_doc.document) {
        iframe_doc = iframe_doc.document;
    }

    iframe_html = "<html><head></head><body><form method='POST' action='" +
                  url +"'>" 

    Object.keys(data).forEach(function(key){
        iframe_html += "<input type='hidden' name='"+key+"' value='"+data[key]+"'>";

    });

        iframe_html +="</form></body></html>";

    iframe_doc.open();
    iframe_doc.write(iframe_html);
    $(iframe_doc).find('form').submit();
}

Utilisez ceci comme ceci:

$('#someid').on('click', function() {
    ajax_download('/download.action', {'para1': 1, 'para2': 2});
});

Les paramètres sont envoyés en tant que paramètres de post appropriés comme s'ils provenaient d'une entrée plutôt que d'une chaîne codée en json comme dans l'exemple précédent.

CAVEAT: Méfiez-vous du potentiel d'injection variable sur ces formes. Il pourrait y avoir un moyen plus sûr d'encoder ces variables. Alternativement envisager de leur échapper.


Voici ce que j'ai fait, javascript pur et html. N'a pas testé, mais cela devrait fonctionner dans tous les navigateurs.

Fonction Javascript

var iframe = document.createElement('iframe');
iframe.id = "IFRAMEID";
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.src = 'SERVERURL'+'?' + $.param($scope.filtro);
iframe.addEventListener("load", function () {
     console.log("FILE LOAD DONE.. Download should start now");
});

En utilisant uniquement des composants pris en charge dans tous les navigateurs, aucune bibliothèque supplémentaire.

Voici mon code de contrôleur JAVA Spring côté serveur.

@RequestMapping(value = "/rootto/my/xlsx", method = RequestMethod.GET)
public void downloadExcelFile(@RequestParam(value = "param1", required = false) String param1,
    HttpServletRequest request, HttpServletResponse response)
            throws ParseException {

    Workbook wb = service.getWorkbook(param1);
    if (wb != null) {
        try {
            String fileName = "myfile_" + sdf.format(new Date());
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + ".xlsx\"");
            wb.write(response.getOutputStream());
            response.getOutputStream().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    }

Personne n'a posté cette solution @ Pekka ... alors je l'afficherai. Cela peut aider quelqu'un.

Vous ne pouvez pas et n'avez pas besoin de le faire via Ajax. Juste utiliser

window.location="download.action?para1=value1...."

Vous pouvez avec HTML5

NB: Les données du fichier retournées DOIVENT être codées en base64 car vous ne pouvez pas encoder des données binaires JSON

Dans ma réponse AJAX , j'ai une structure de données qui ressemble à ceci:

{
    result: 'OK',
    download: {
        mimetype: string(mimetype in the form 'major/minor'),
        filename: string(the name of the file to download),
        data: base64(the binary data as base64 to download)
    }
}

Cela signifie que je peux faire ce qui suit pour enregistrer un fichier via AJAX

var a = document.createElement('a');
if (window.URL && window.Blob && ('download' in a) && window.atob) {
    // Do it the HTML5 compliant way
    var blob = base64ToBlob(result.download.data, result.download.mimetype);
    var url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = result.download.filename;
    a.click();
    window.URL.revokeObjectURL(url);
}

La fonction base64ToBlob a été prise d' here et doit être utilisée conformément à cette fonction

function base64ToBlob(base64, mimetype, slicesize) {
    if (!window.atob || !window.Uint8Array) {
        // The current browser doesn't have the atob function. Cannot continue
        return null;
    }
    mimetype = mimetype || '';
    slicesize = slicesize || 512;
    var bytechars = atob(base64);
    var bytearrays = [];
    for (var offset = 0; offset < bytechars.length; offset += slicesize) {
        var slice = bytechars.slice(offset, offset + slicesize);
        var bytenums = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
            bytenums[i] = slice.charCodeAt(i);
        }
        var bytearray = new Uint8Array(bytenums);
        bytearrays[bytearrays.length] = bytearray;
    }
    return new Blob(bytearrays, {type: mimetype});
};

C'est bon si votre serveur est en train de déposer des fichiers à sauvegarder. Cependant, je n'ai pas tout à fait compris comment on pourrait implémenter un HTML4 fallback


Bluish fait raison à ce sujet, vous ne pouvez pas le faire via Ajax car JavaScript ne peut pas enregistrer les fichiers directement sur l'ordinateur d'un utilisateur (pour des raisons de sécurité). Malheureusement, le fait de pointer l'URL de la fenêtre principale lors du téléchargement de votre fichier signifie que vous avez peu de contrôle sur l'expérience de l'utilisateur lors du téléchargement d'un fichier.

J'ai créé le téléchargement de fichiers jQuery qui permet une expérience "Ajax like" avec des téléchargements de fichiers complets avec des rappels OnSuccess et OnFailure pour fournir une meilleure expérience utilisateur. Jetez un oeil à mon blog sur le problème commun que le plugin résout et quelques façons de l'utiliser et aussi une démo de jQuery File Download en action . Voici la source

Voici une démo de cas d'utilisation simple utilisant la source plugin avec des promesses. La page de démonstration comprend également de nombreux autres exemples de «meilleurs UX».

$.fileDownload('some/file.pdf')
    .done(function () { alert('File download a success!'); })
    .fail(function () { alert('File download failed!'); });

En fonction des navigateurs que vous devez prendre en charge, vous pouvez utiliser https://github.com/eligrey/FileSaver.js/ qui permet un contrôle plus explicite que la méthode IFRAME que jQuery File Download utilise.





download