web-dev-qa-db-fra.com

Javascript: Téléchargement d'un fichier ... sans fichier

J'essaie de simuler un téléchargement de fichier sans utiliser réellement une entrée de fichier de l'utilisateur. Le contenu du fichier sera généré dynamiquement à partir d'une chaîne.

Est-ce possible? Quelqu'un l'a-t-il déjà fait auparavant? Existe-t-il des exemples/théories?

Pour clarifier, je sais comment télécharger un fichier en utilisant AJAX techniques utilisant un iframe caché et des amis - le problème est de télécharger un fichier qui n'est pas dans le formulaire.

J'utilise ExtJS, mais jQuery est également possible car ExtJS peut s'y connecter (ext-jquery-base).

51
LiraNuna

Pourquoi ne pas simplement utiliser XMLHttpRequest() avec POST?

function beginQuoteFileUnquoteUpload(data)
{
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "http://www.mysite.com/myuploadhandler.php", true);
    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xhr.onreadystatechange = function ()
    {
        if (xhr.readyState == 4 && xhr.status == 200)
            alert("File uploaded!");
    }
    xhr.send("filedata="+encodeURIComponent(data));
}

Le script du gestionnaire sur le serveur écrit simplement les données du fichier dans un fichier.

MODIFIER
Le téléchargement de fichiers est toujours une publication http avec un type de contenu différent. Vous pouvez utiliser ce type de contenu et séparer votre contenu avec des limites:

function beginQuoteFileUnquoteUpload(data)
{
    // Define a boundary, I stole this from IE but you can use any string AFAIK
    var boundary = "---------------------------7da24f2e50046";
    var xhr = new XMLHttpRequest();
    var body = '--' + boundary + '\r\n'
             // Parameter name is "file" and local filename is "temp.txt"
             + 'Content-Disposition: form-data; name="file";'
             + 'filename="temp.txt"\r\n'
             // Add the file's mime-type
             + 'Content-type: plain/text\r\n\r\n'
             + data + '\r\n'
             + boundary + '--';

    xhr.open("POST", "http://www.mysite.com/myuploadhandler.php", true);
    xhr.setRequestHeader(
        "Content-type", "multipart/form-data; boundary="+boundary

    );
    xhr.onreadystatechange = function ()
    {
        if (xhr.readyState == 4 && xhr.status == 200)
            alert("File uploaded!");
    }
    xhr.send(body);
}

Si vous souhaitez envoyer des données supplémentaires, il vous suffit de séparer chaque section avec une limite et de décrire les en-têtes de disposition de contenu et de type de contenu pour chaque section. Chaque en-tête est séparé par une nouvelle ligne et le corps est séparé des en-têtes par une nouvelle ligne supplémentaire. Naturellement, le téléchargement de données binaires de cette manière serait un peu plus difficile :-)

Autre modification: oublié de mentionner, assurez-vous que la chaîne de limite ne se trouve pas dans le "fichier" texte que vous envoyez, sinon elle sera traitée comme une limite.

34
Andy E

Si vous n'avez pas besoin de prise en charge pour les anciens navigateurs, vous pouvez utiliser l'objet FormData, qui fait partie de l'API de fichier:

var formData = new FormData();
var blob = new Blob(['Lorem ipsum'], { type: 'plain/text' });
formData.append('file', blob,'readme.txt');

var request = new XMLHttpRequest();
request.open('POST', 'http://example.org/upload');
request.send(formData);

File Api est pris en charge par tous les navigateurs actuels (IE10 +)

33
Josa

Il suffit de partager le résultat final, qui fonctionne - et a une manière propre d'ajouter/supprimer des paramètres sans rien coder en dur.

var boundary = '-----------------------------' +
            Math.floor(Math.random() * Math.pow(10, 8));

    /* Parameters go here */
var params = {
    file: {
        type: 'text/plain',
        filename: Path.utils.basename(currentTab.id),
        content: GET_CONTENT() /* File content goes here */
    },
    action: 'upload',
    overwrite: 'true',
    destination: '/'
};

var content = [];
for(var i in params) {
    content.Push('--' + boundary);

    var mimeHeader = 'Content-Disposition: form-data; name="'+i+'"; ';
    if(params[i].filename)
        mimeHeader += 'filename="'+ params[i].filename +'";';
    content.Push(mimeHeader);

    if(params[i].type)
        content.Push('Content-Type: ' + params[i].type);

    content.Push('');
    content.Push(params[i].content || params[i]);
};

    /* Use your favorite toolkit here */
    /* it should still work if you can control headers and POST raw data */
Ext.Ajax.request({
    method: 'POST',
    url: 'www.example.com/upload.php',
    jsonData: content.join('\r\n'),
    headers: {
        'Content-Type': 'multipart/form-data; boundary=' + boundary,
        'Content-Length': content.length
    }
});

Cela a été testé pour fonctionner sur tous les navigateurs modernes, y compris mais sans s'y limiter:

  • IE6 +
  • FF 1.5+
  • Opera 9+
  • Chrome 1.0+
  • Safari 3.0+
13
LiraNuna

Un téléchargement de fichier est juste une demande POST avec ce contenu de fichier correctement encodé et avec un en-tête spécial multipart/formdata. Vous devez utiliser ce <input type=file /> Car la sécurité de votre navigateur vous interdit d'accéder directement au disque utilisateur.

Comme vous n'avez pas besoin de lire le disque utilisateur, [~ # ~] oui [~ # ~] , vous pouvez le simuler en utilisant Javascript. Ce sera juste un XMLHttpRequest. Pour forger une demande de téléchargement "authentique", vous pouvez installer Fiddler et inspecter votre demande sortante.

Vous devrez encoder ce fichier correctement, donc ce lien peut être très utile: RFC 2388: Renvoyer des valeurs à partir de formulaires: multipart/form-data

6
Rubens Farias

Je viens d'attraper cette chaîne POST_DATA avec l'addon Firefox TamperData. J'ai soumis un formulaire avec un type="file" champ nommé "monfichier" et un bouton d'envoi nommé "btn-submit" avec la valeur "Upload". Le contenu du fichier téléchargé est

Line One
Line Two
Line Three

Voici donc la chaîne POST_DATA:

-----------------------------192642264827446\r\n
Content-Disposition: form-data;    \n
name="myfile"; filename="local-file-name.txt"\r\n
Content-Type: text/plain\r\n
\r\n
Line \n
One\r\n
Line Two\r\n
Line Three\r\n
\r\n
-----------------------------192642264827446\n
\r\n
Content-Disposition: form-data; name="btn-submit"\r\n
\r\n
Upload\n
\r\n
-----------------------------192642264827446--\r\n

Je ne sais pas ce que signifie le nombre (192642264827446), mais cela ne devrait pas être trop difficile à découvrir.

3
Tom Bartel

Un moyen facile d'imiter le "faux" téléchargement de fichiers avec jQuery:

var fd = new FormData();
var file = new Blob(['file contents'], {type: 'plain/text'});

fd.append('formFieldName', file, 'fileName.txt');

$.ajax({
  url: 'http://example.com/yourAddress',
  method: 'post',
  data: fd,
  processData: false,        //this...
  contentType: false         //and this is for formData type
});
3
ixpl0

https://stackoverflow.com/a/2198524/2914587 a fonctionné pour moi, après avoir ajouté un '--' supplémentaire avant le boundary final dans la charge utile:

var body = '--' + boundary + '\r\n'
         // Parameter name is "file" and local filename is "temp.txt"
         + 'Content-Disposition: form-data; name="file";'
         + 'filename="temp.txt"\r\n'
         // Add the file's mime-type
         + 'Content-type: plain/text\r\n\r\n'
         + data + '\r\n'
         + '--' + boundary + '--';