Als ik een eenvoudig formulier als dit indien met een bijgevoegd bestand:
<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>
Hoe wordt het bestand intern verzonden? Wordt het bestand verzonden als onderdeel van de HTTP-body als gegevens? In de headers van dit verzoek zie ik niets met betrekking tot de naam van het bestand.
Ik zou graag de interne werking van HTTP willen weten bij het verzenden van een bestand.
Antwoord 1, autoriteit 100%
Laten we eens kijken wat er gebeurt als je een bestand selecteert en je formulier verzendt (ik heb de koppen afgekapt voor de beknoptheid):
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--
OPMERKING: elke grensreeks moet worden voorafgegaan door een extra --
, net als aan het einde van de laatste grensreeks. Het bovenstaande voorbeeld bevat dit al, maar het kan gemakkelijk over het hoofd worden gezien. Zie commentaar van @Andreas hieronder.
In plaats van URL-codering van de formulierparameters, worden de formulierparameters (inclusief de bestandsgegevens) verzonden als secties in een meerdelig document in de hoofdtekst van het verzoek.
In het bovenstaande voorbeeld ziet u de invoer MAX_FILE_SIZE
met de waarde ingesteld in het formulier, evenals een sectie die de bestandsgegevens bevat. De bestandsnaam maakt deel uit van de Content-Disposition
header.
De volledige details zijn hier.
Antwoord 2, autoriteit 89%
Hoe verzendt het het bestand intern?
Het formaat heet multipart/form-data
, zoals gevraagd op: Wat betekent enctype=’multipart/form-data’?
Ik ga:
- voeg nog wat HTML5-referenties toe
- leg uit waaromhij gelijk heeft met een voorbeeld voor het indienen van een formulier
HTML5-referenties
Er zijn drie mogelijkhedenvoor enctype
:
x-www-urlencoded
multipart/form-data
(specificatie verwijst naar RFC2388)text-plain
. Dit is “niet betrouwbaar interpreteerbaar door de computer”, dus het mag nooit in productie worden gebruikt en we zullen er niet verder naar kijken.
Hoe de voorbeelden te genereren
Zodra je een voorbeeld van elke methode ziet, wordt het duidelijk hoe ze werken en wanneer je ze allemaal moet gebruiken.
U kunt voorbeelden maken met:
nc -l
of een ECHO-server: HTTP-testserver accepteert GET/POST-verzoeken- een user-agent zoals een browser of cURL
Sla het formulier op in een minimaal .html
-bestand:
<!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ω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>
We hebben de standaard tekstwaarde ingesteld op aωb
, wat aωb
betekent omdat ω
U+03C9
, dit zijn de bytes 61 CF 89 62
in UTF-8.
Maak bestanden om te uploaden:
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
Ren onze kleine echo-server:
while true; do printf '' | nc -l 8000 localhost; done
Open de HTML in uw browser, selecteer de bestanden en klik op Verzenden en controleer de terminal.
nc
Drukt het ontvangen aanvraag af.
getest op: ubuntu 14.04.3, nc
BSD 1.105, Firefox 40.
Multipart / Form-gegevens
Firefox verzonden:
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--
Voor het binaire bestands- en tekstveld worden de bytes 61 CF 89 62
(aωb
in UTF-8) letterlijk verzonden. Je kunt dat verifiëren met nc -l localhost 8000 | hd
, die zegt dat de bytes:
61 CF 89 62
werden verzonden (61
== ‘A’ en 62
== ‘B’).
Daarom is het duidelijk dat:
-
Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150
Stelt het inhoudstype in opmultipart/form-data
en zegt dat de velden worden gescheiden door de gegevenboundary
string.Merk op dat de:
boundary=---------------------------735323031399963166993862150
heeft twee minder dadhes
--
dan de werkelijke barrière-----------------------------735323031399963166993862150
Dit komt omdat de standaard de grens vereist om te beginnen met twee streepjes
--
. De andere streepjes lijken precies hoe Firefox ervoor koos om de willekeurige grens te implementeren. RFC 7578 vermeldt duidelijk dat die twee toonaangevende streepjes--
vereist zijn:4.1. Parameter “Grens” van Multipart / Form-gegevens
Zoals met andere multipart-typen, zijn de onderdelen gescheiden met een
Grensscheidingsteken, geconstrueerd met behulp van CRLF, “-“, en de waarde van
de parameter “Grens”. -
Elk veld krijgt een aantal subkoppen vóór de gegevens:
Content-Disposition: form-data;
, het veldname
, DEfilename
, gevolgd door de gegevens.De server leest de gegevens tot de volgende grensstring. De browser moet een grens kiezen die niet in een van de velden verschijnt, dus dit is de reden waarom de grens tussen aanvragen kan variëren.
Omdat we de unieke grens hebben, is geen codering van de gegevens noodzakelijk: binaire gegevens worden verzonden zoals.
TODO: wat is de optimale grensgrootte (
log(N)
ik wed), en naam/looptijd van het algoritme dat het vindt? Gevraagd op: https://cs.stackexchange.com/questions/39687/find-the-shortest-sequence-that-is-not-a-sub-sequence-of-a-set-of-sequences -
Content-Type
wordt automatisch bepaald door de browser.Hoe het precies wordt bepaald, is gevraagd op: Hoe wordt het mime-type van een geüpload bestand bepaald door de browser?
application/x-www-form-urlencoded
Verander nu het enctype
in application/x-www-form-urlencoded
, laad de browser opnieuw en verzend opnieuw.
Firefox verzonden:
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
Het is duidelijk dat de bestandsgegevens niet zijn verzonden, alleen de basisnamen. Dit kan dus niet worden gebruikt voor bestanden.
Wat het tekstveld betreft, zien we dat gebruikelijke afdrukbare tekens zoals a
en b
in één byte werden verzonden, terwijl niet-afdrukbare tekens zoals 0xCF
en 0x89
namen elk 3 bytesin beslag: %CF%89
!
Vergelijking
Bestandsuploads bevatten vaak veel niet-afdrukbare tekens (bijv. afbeeldingen), terwijl tekstformulieren dat bijna nooit doen.
Van de voorbeelden die we hebben gezien dat:
-
multipart/form-data
: voegt een paar bytes aan grensoverhead aan het bericht toe en moet wat tijd besteden aan het berekenen ervan, maar verzendt elke byte in één byte. -
application/x-www-form-urlencoded
: heeft een grens van één byte per veld (&
), maar voegt een lineaireoverheadfactor van 3xvoor elk niet-afdrukbaar teken.
Dus zelfs als we bestanden zouden kunnen verzenden met application/x-www-form-urlencoded
, zouden we dat niet willen, omdat het zo inefficiënt is.
Maar voor afdrukbare tekens die in tekstvelden worden gevonden, maakt het niet uit en genereert het minder overhead, dus gebruiken we het gewoon.
Antwoord 3, autoriteit 20%
Bestand verzenden als binaire inhoud (upload zonder formulier of FormData)
In de gegeven antwoorden/voorbeelden is het bestand (waarschijnlijk) geüpload met een HTML-formulier of met behulp van de FormData-API. Het bestand is slechts een deel van de gegevens die in het verzoek zijn verzonden, vandaar de kop multipart/form-data
Content-Type
.
Als u het bestand als enige inhoud wilt verzenden, kunt u het direct toevoegen als de hoofdtekst van het verzoek en stelt u de Content-Type
-header in op het MIME-type van het bestand dat u verzendt. De bestandsnaam kan worden toegevoegd in de Content-Disposition
header. Je kunt als volgt uploaden:
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);
Als je geen formulieren wilt (willen) gebruiken en je bent alleen geïnteresseerd in het uploaden van één enkel bestand, dan is dit de gemakkelijkste manier om je bestand op te nemen in het verzoek.
Antwoord 4, Autoriteit 3%
Ik heb deze voorbeeld Java-code:
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
public class TestClass {
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocket(8081);
Socket accept = socket.accept();
InputStream inputStream = accept.getInputStream();
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);
}
}
en ik heb dit test.html-bestand:
<!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>
En ten slotte het bestand dat ik zal gebruiken voor testdoeleinden, genaamd A.DAT heeft de volgende inhoud:
0x39 0x69 0x65
Als u de bytes hierboven interpreteert als ASCII- of UTF-8-tekens, zullen ze daadwerkelijk vertegenwoordigen:
9ie
Dus laten we onze Java-code uitvoeren, open Test.html in onze favoriete browser, upload a.dat
en verzend het formulier en zie wat onze server ontvangt :
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--
Nou, het verbaast me niet om de tekens 9iete zien, want we hebben Java gezegd ze af te drukken en ze te behandelen als UTF-8-tekens. Je kunt er net zo goed voor kiezen om ze te lezen als onbewerkte bytes..
Cookie: JSESSIONID=27D0A0637A0449CF65B3CB20F40048AF
is eigenlijk de laatste HTTP-header hier. Daarna komt de HTTP-body, waar de meta en de inhoud van het bestand dat we hebben geüpload daadwerkelijk te zien zijn.
Antwoord 5, autoriteit 2%
Een HTTP-bericht kan een aantal gegevens bevatten die na de kopregels worden verzonden. In een reactie wordt hier de gevraagde bron teruggestuurd naar de client (het meest voorkomende gebruik van de berichttekst), of misschien een verklarende tekst als er een fout is. In een verzoek worden hier door de gebruiker ingevoerde gegevens of geüploade bestanden naar de server gestuurd.