Comment fonctionne le téléchargement de fichiers HTTP?

quand je soumets un formulaire simple comme celui-ci avec un dossier joint:

<form enctype="multipart/form-data" action="http://localhost:3000/upload?upload_progress_id=12344" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="100000" />
Choose a file to upload: <input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>

comment envoie-t-il le fichier en interne? Le fichier est-il envoyé en tant que données dans le corps HTTP? Dans les en-têtes de cette requête, Je ne vois rien de relié au nom du fichier.

je voudrais juste connaître le fonctionnement interne du HTTP lors de l'envoi d'un fichier.

397
demandé sur Mark Amery 2011-12-28 22:34:34

5 réponses

regardons ce qui se passe quand vous sélectionnez un fichier et soumettez votre formulaire (j'ai tronqué les en-têtes pour la brièveté):

POST /upload?upload_progress_id=12344 HTTP/1.1
Host: localhost:3000
Content-Length: 1325
Origin: http://localhost:3000
... other headers ...
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryePkpFF7tjBAqx29L

------WebKitFormBoundaryePkpFF7tjBAqx29L
Content-Disposition: form-data; name="MAX_FILE_SIZE"

100000
------WebKitFormBoundaryePkpFF7tjBAqx29L
Content-Disposition: form-data; name="uploadedfile"; filename="hello.o"
Content-Type: application/x-object

... contents of file goes here ...
------WebKitFormBoundaryePkpFF7tjBAqx29L--

au lieu d'une URL encodant les paramètres de la forme, les paramètres de la forme (y compris les données du fichier) sont envoyés sous forme de sections dans un document multipartie dans le corps de la requête.

Dans l'exemple ci-dessus, vous pouvez voir l'entrée MAX_FILE_SIZE avec la valeur définie dans le formulaire, ainsi qu'une section contenant le fichier données. Le nom du fichier fait partie de l'en-tête Content-Disposition .

les détails complets sont ici .

220
répondu toddsundsted 2015-07-24 12:50:23

comment envoie-t-il le fichier en interne?

le format est appelé multipart/form-data , comme demandé à: que signifie enctype= 'multipart / form-data'?

je vais à:

  • ajouter un peu plus de références HTML5
  • expliquer pourquoi il a raison avec un formulaire soumettre un exemple

HTML5 references

il y a trois possibilités pour enctype :

comment générer les exemples

une fois que vous voyez un exemple de chaque méthode, il devient évident comment ils fonctionnent, et quand vous devriez utiliser chacun d'eux.

vous pouvez produire des exemples en utilisant:

sauvegardez le formulaire dans un fichier .html minimal:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <title>upload</title>
</head>
<body>
  <form action="http://localhost:8000" method="post" enctype="multipart/form-data">
  <p><input type="text" name="text1" value="text default">
  <p><input type="text" name="text2" value="a&#x03C9;b">
  <p><input type="file" name="file1">
  <p><input type="file" name="file2">
  <p><input type="file" name="file3">
  <p><button type="submit">Submit</button>
</form>
</body>
</html>

nous avons défini la valeur par défaut du texte à a&#x03C9;b , qui signifie aωb parce que ω est U+03C9 , qui sont les octets 61 CF 89 62 dans UTF-8.

créer des fichiers pour télécharger:

echo 'Content of a.txt.' > a.txt

echo '<!DOCTYPE html><title>Content of a.html.</title>' > a.html

# Binary file containing 4 bytes: 'a', 1, 2 and 'b'.
printf 'a\xCF\x89b' > binary

Lancez notre petit serveur echo:

while true; do printf '' | nc -l 8000 localhost; done

ouvrez le HTML de votre navigateur, sélectionnez les fichiers et cliquez sur Soumettre et vérifiez le terminal.

nc imprime la demande reçue.

testé sur Ubuntu 14.04.3, nc BSD 1.105, Firefox 40.

multipart /form-data

Firefox envoyé:

POST / HTTP/1.1
[[ Less interesting headers ... ]]
Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150
Content-Length: 834

-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="text1"

text default
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="text2"

aωb
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file1"; filename="a.txt"
Content-Type: text/plain

Content of a.txt.

-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file2"; filename="a.html"
Content-Type: text/html

<!DOCTYPE html><title>Content of a.html.</title>

-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file3"; filename="binary"
Content-Type: application/octet-stream

aωb
-----------------------------735323031399963166993862150--

Pour le fichier binaire et le champ de texte, les octets 61 CF 89 62 ( aωb en UTF-8) sont envoyés à la lettre. Vous pouvez vérifier cela avec nc -l localhost 8000 | hd , qui dit que les octets:

61 CF 89 62

ont été envoyés ( 61 = = " a " et 62 == 'b').

il est donc clair que:

  • Content-Type: multipart/form-data; boundary=---------------------------9051914041544843365972754266 définit le type de contenu à multipart/form-data et indique que les champs sont séparés par la chaîne de caractères boundary .

  • chaque champ reçoit des sous-en-têtes avant ses données: Content-Disposition: form-data; , le champ name , le filename , suivi des données.

    le serveur lit les données jusqu'à la chaîne de limite suivante. Le navigateur doit choisir une limite qui n'apparaissent pas dans les champs, c'est pourquoi la limite peut varier entre les demandes.

    parce que nous avons la frontière unique, aucun encodage des données n'est nécessaire: les données binaires sont envoyées telles quelles.

    TODO: Quelle est la taille optimale de la limite ( log(N) je parie), et le nom / la durée d'exécution de l'algorithme qui le trouve? Demandé à: https://cs.stackexchange.com/questions/39687/find-the-shortest-sequence-that-is-not-a-sub-sequence-of-a-set-of-sequences

  • Content-Type est automatiquement déterminé par le navigateur.

    Comment ça est déterminé exactement a été demandé à: comment le type mime d'un fichier téléchargé est-il déterminé par navigateur?

application / x-www-form-urlencoded

maintenant changer le enctype en application/x-www-form-urlencoded , recharger le navigateur, et soumettre à nouveau.

Firefox envoyé:

POST / HTTP/1.1
[[ Less interesting headers ... ]]
Content-Type: application/x-www-form-urlencoded
Content-Length: 51

text1=text+default&text2=a%CF%89b&file1=a.txt&file2=a.html&file3=binary

il est clair que les données du fichier n'ont pas été envoyées, seulement les noms de base. Donc cela ne peut pas être utilisé pour fichier.

quant au champ de texte, nous voyons que les caractères imprimables habituels comme a et b ont été envoyés en un octet, tandis que les caractères non imprimables comme 0xCF et 0x89 ont pris 3 octets chacun: %CF%89 !

comparaison

Les téléchargements de fichiers

contiennent souvent beaucoup de caractères non imprimables (par exemple des images), alors que les formes de texte ne le font presque jamais.

Des exemples nous avons vu que:

  • multipart/form-data : ajoute quelques bytes de limite supérieure au message, et doit passer un certain temps à le calculer, mais envoie chaque byte dans un byte.

  • application/x-www-form-urlencoded : a une limite de byte unique par champ ( & ), mais ajoute un linéaire facteur de plafond de 3x pour chaque caractère non imprimable.

donc, même si nous pouvions envoyer des fichiers avec application/x-www-form-urlencoded , nous ne le voudrions pas, parce que c'est tellement inefficace.

mais pour les caractères imprimables trouvés dans les champs de texte, cela n'a pas d'importance et génère moins de frais généraux, donc nous l'utilisons.

181

envoyer le fichier sous forme de contenu binaire (upload sans forme ou FormData)

dans les réponses/exemples donnés, le fichier est (très probablement) téléchargé avec un format HTML ou en utilisant FormData API . Le fichier n'est qu'une partie des données envoyées dans la requête, d'où l'en-tête multipart/form-data Content-Type .

si vous voulez envoyer le fichier comme seul contenu, alors vous pouvez l'ajouter directement comme le corps de la requête et vous définissez le Content-Type l'en-tête du type MIME du fichier que vous envoyez. Le nom du fichier peut être ajouté dans l'en-tête Content-Disposition . Vous pouvez télécharger comme ceci:

var xmlHttpRequest = new XMLHttpRequest();

var file = ...file handle...
var fileName = ...file name...
var target = ...target...
var mimeType = ...mime type...

xmlHttpRequest.open('POST', target, true);
xmlHttpRequest.setRequestHeader('Content-Type', mimeType);
xmlHttpRequest.setRequestHeader('Content-Disposition', 'attachment; filename="' + fileName + '"');
xmlHttpRequest.send(file);

si vous n'utilisez pas les formulaires et que vous ne souhaitez télécharger qu'un seul fichier, c'est la façon la plus simple d'inclure votre fichier dans la demande.

42
répondu Wilt 2016-02-11 08:00:35

j'ai cet exemple de code Java:

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
public class TestClass {
    public static void main(String[] args) throws IOException {
        final ServerSocket socket = new ServerSocket(8081);
        final Socket accept = socket.accept();
        final InputStream inputStream = accept.getInputStream();
        final InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
        char readChar;
        while ((readChar = (char) inputStreamReader.read()) != -1) {
            System.out.print(readChar);
        }
        inputStream.close();
        accept.close();
        System.exit(1);
    }
}

et j'ai ce test.fichier html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>File Upload!</title>
</head>
<body>
<form method="post" action="http://localhost:8081" enctype="multipart/form-data">
    <input type="file" name="file" id="file">
    <input type="submit">
</form>
</body>
</html>

et enfin le fichier que j'utiliserai à des fins de test, nommé A. dat a le contenu suivant:

0x39 0x69 0x65

si vous interprétez les octets ci-dessus comme des caractères ASCII ou UTF-8, ils représenteront en fait:

9ie

alors lançons notre Java Code, ouvrez le test .html dans notre navigateur préféré, téléchargez a.dat et soumettez le formulaire et voir ce que notre serveur reçoit:

POST / HTTP/1.1
Host: localhost:8081
Connection: keep-alive
Content-Length: 196
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: null
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.97 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary06f6g54NVbSieT6y
DNT: 1
Accept-Encoding: gzip, deflate
Accept-Language: en,en-US;q=0.8,tr;q=0.6
Cookie: JSESSIONID=27D0A0637A0449CF65B3CB20F40048AF

------WebKitFormBoundary06f6g54NVbSieT6y
Content-Disposition: form-data; name="file"; filename="a.dat"
Content-Type: application/octet-stream

9ie
------WebKitFormBoundary06f6g54NVbSieT6y--

Eh bien, je ne suis pas surpris de voir les caractères 9ie parce que nous avons dit à Java de les imprimer en les traitant comme des caractères UTF-8. Vous pouvez aussi bien choisir de les lire en tant qu'octets bruts..

Cookie: JSESSIONID=27D0A0637A0449CF65B3CB20F40048AF 

est en fait le dernier en-tête HTTP ici. Après cela vient le Corps HTTP, où méta et le contenu du fichier que nous avons téléchargé peuvent être vus.

7
répondu Koray Tugay 2018-07-26 19:23:16

un message HTTP peut avoir un corps de données envoyé après les lignes d'en-tête. Dans une réponse, c'est là que la ressource demandée est retournée au client (l'utilisation la plus courante du corps du message), ou peut-être un texte explicatif s'il y a une erreur. Dans une demande, c'est où les données saisies par l'utilisateur ou les fichiers téléchargés sont envoyées au serveur.

http://www.tutorialspoint.com/http/http_messages.htm

5
répondu flagg19 2011-12-28 18:42:26