745 Stimmen

Wie funktioniert der HTTP-Datei-Upload?

Wenn ich ein einfaches Formular wie dieses mit einer angehängten Datei absende:

<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>

Wie wird die Datei intern versendet? Wird die Datei als Teil des HTTP-Bodys als Daten gesendet? In den Kopfzeilen dieser Anfrage sehe ich nichts, was sich auf den Namen der Datei bezieht.

Ich möchte nur wissen, wie HTTP intern funktioniert, wenn ich eine Datei sende.

0 Stimmen

Ich habe schon lange keinen Sniffer mehr benutzt, aber wenn Sie sehen wollen, was in Ihrer Anfrage gesendet wird (da sie an den Server gerichtet ist, ist es eine Anfrage), dann schnüffeln Sie. Diese Frage ist zu weit gefasst. SO ist mehr für spezifische Programmierfragen.

0 Stimmen

...als Schnüffler, . ist die Waffe meiner Wahl. Sie können sogar Ihre eigenen Testanfragen erstellen, um zu sehen, wie sie veröffentlicht werden.

0 Stimmen

Für Interessierte, siehe auch " MAX_FILE_SIZE in PHP - wozu das Ganze?" auf stackoverflow.com/q/1381364/632951

416voto

toddsundsted Punkte 6025

Schauen wir uns einmal an, was passiert, wenn Sie eine Datei auswählen und Ihr Formular abschicken (ich habe die Kopfzeilen der Kürze halber gekürzt):

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--

HINWEIS: Jeder Begrenzungszeichenfolge muss ein zusätzliches -- genau wie am Ende der letzten Begrenzungszeichenfolge. Das obige Beispiel enthält dies bereits, aber es kann leicht übersehen werden. Siehe Kommentar von @Andreas unten.

Anstelle der URL-Kodierung der Formularparameter werden die Formularparameter (einschließlich der Dateidaten) als Abschnitte eines mehrteiligen Dokuments im Hauptteil der Anfrage gesendet.

Im obigen Beispiel können Sie die Eingabe sehen MAX_FILE_SIZE mit dem im Formular eingestellten Wert sowie einem Abschnitt mit den Dateidaten. Der Dateiname ist Teil der Content-Disposition Kopfzeile.

Die vollständigen Angaben sind aquí .

0 Stimmen

Bedeutet dies, dass Port 80 (oder der Port, der http-Anfragen bedient) während der Zeit der Dateiübertragung unbrauchbar ist? Wenn z. B. eine große Datei (etwa ein GB) hochgeladen wird, kann der Webserver in dieser Zeit keine anderen Anfragen beantworten?

8 Stimmen

@source.rar: Nein. Webserver sind (fast?) immer mit Threads versehen, so dass sie gleichzeitige Verbindungen verarbeiten können. Im Wesentlichen übergibt der Daemon-Prozess, der an Port 80 lauscht, die Aufgabe des Servierens sofort an einen anderen Thread/Prozess, damit er wieder auf eine andere Verbindung warten kann; selbst wenn zwei eingehende Verbindungen genau zum selben Zeitpunkt eintreffen, bleiben sie im Netzwerkpuffer liegen, bis der Daemon bereit ist, sie zu lesen.

15 Stimmen

Die Erklärung zum Threading ist nicht ganz korrekt, da es Hochleistungsserver gibt, die als Single-Thread-Server konzipiert sind und einen Zustandsautomaten verwenden, um Datenpakete von Verbindungen schnell abwechselnd herunterzuladen. Bei TCP/IP ist der Port 80 ein Abhörport und nicht der Port, über den die Daten übertragen werden.

385voto

Wie wird die Datei intern versendet?

Das Format heißt multipart/form-data wie gefragt bei: Was bedeutet enctype='multipart/form-data'?

Das werde ich tun:

  • einige weitere HTML5-Referenzen hinzufügen
  • erklären warum er hat Recht mit einem Beispiel für die Übermittlung eines Formulars

HTML5-Referenzen

Es gibt drei Möglichkeiten para enctype :

Wie man die Beispiele generiert

Sobald Sie ein Beispiel für jede Methode sehen, wird klar, wie sie funktionieren und wann Sie sie einsetzen sollten.

Sie können Beispiele erstellen:

Speichern Sie das Formular in einem minimalen .html archivo:

<!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>

Wir setzen den Standard-Textwert auf a&#x03C9;b was bedeutet ab denn es U+03C9 das sind die Bytes 61 CF 89 62 in UTF-8.

Erstellen Sie Dateien zum Hochladen:

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

Starten Sie unseren kleinen Echo-Server:

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

Öffnen Sie den HTML-Code in Ihrem Browser, wählen Sie die Dateien aus, klicken Sie auf "Senden" und überprüfen Sie das Terminal.

nc druckt die erhaltene Anfrage.

Getestet am: Ubuntu 14.04.3, nc BSD 1.105, Firefox 40.

multipart/form-data

Firefox gesendet:

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"

ab
-----------------------------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

ab
-----------------------------735323031399963166993862150--

Für die Binärdatei und das Textfeld werden die Bytes 61 CF 89 62 ( ab in UTF-8) werden wörtlich gesendet. Sie können dies überprüfen mit nc -l localhost 8000 | hd die besagt, dass die Bytes:

61 CF 89 62

gesendet wurden ( 61 == 'a' und 62 == 'b').

Es ist also klar, dass:

  • Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150 setzt den Inhaltstyp auf multipart/form-data und sagt, dass die Felder durch die angegebene boundary String.

    Aber beachten Sie, dass die:

    boundary=---------------------------735323031399963166993862150

    hat zwei Daddeln weniger -- als die eigentliche Barriere

    -----------------------------735323031399963166993862150

    Dies liegt daran, dass die Norm vorschreibt, dass die Begrenzung mit zwei Bindestrichen beginnen muss -- . Die anderen Striche scheinen nur die Art und Weise zu sein, wie Firefox die willkürliche Grenze implementiert hat. RFC 7578 erwähnt eindeutig, dass diese beiden führenden Bindestriche -- sind erforderlich:

4.1. "Boundary" Parameter von multipart/form-data

Wie bei anderen mehrteiligen Typen werden die Teile mit einem Begrenzungszeichen abgegrenzt, das aus CRLF, "--" und dem Wert des des Parameters "Begrenzung".

anwendung/x-www-form-urlencoded

Ändern Sie nun die enctype a application/x-www-form-urlencoded , laden Sie den Browser neu und senden Sie ihn erneut ab.

Firefox gesendet:

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

Offensichtlich wurden die Dateidaten nicht gesendet, sondern nur die Basisnamen. Dies kann also nicht für Dateien verwendet werden.

Was das Textfeld betrifft, so sehen wir, dass übliche druckbare Zeichen wie a y b wurden in einem Byte gesendet, während nicht druckbare Zeichen wie 0xCF y 0x89 aufgegriffen 3 Bytes jeder: %CF%89 !

Vergleich

Datei-Uploads enthalten oft viele nicht druckbare Zeichen (z. B. Bilder), während dies bei Textformularen fast nie der Fall ist.

Anhand der Beispiele haben wir gesehen, dass:

  • multipart/form-data : fügt der Nachricht ein paar Bytes Rand-Overhead hinzu und muss einige Zeit für die Berechnung aufwenden, sendet aber jedes Byte in einem Byte.

  • application/x-www-form-urlencoded : hat eine einzelne Byte-Grenze pro Feld ( & ), sondern fügt eine linear Zuschlagsfaktor von 3x für jedes nicht druckbare Zeichen.

Selbst wenn wir also Dateien mit application/x-www-form-urlencoded Wir würden das nicht wollen, weil es so ineffizient ist.

Für druckbare Zeichen in Textfeldern spielt dies jedoch keine Rolle und verursacht weniger Overhead, so dass wir es einfach verwenden.

2 Stimmen

Wie würden Sie einen binären Anhang hinzufügen? (d.h. ein kleines Bild) - Ich kann mir vorstellen, die Werte für die Content-Disposition y Content-Type Attribute, aber wie geht man mit dem "Inhalt" um?

0 Stimmen

@ochi Ich verstehe das nicht ganz: AFAIK werden die Binärdaten einfach Byte für Byte eingefügt und dein Browser stellt sicher, dass die Trennbyte-Sequenz nicht darin enthalten ist.

0 Stimmen

Ja, ich meinte, wie man die binären Daten erhält, um sie als Inhalt einzufügen. Ich habe versucht, einige base64-kodierte Zeichenketten in die Anfrage (in soapui) einzufügen, aber das geht nicht, also frage ich mich, ob es andere Methoden gibt

87voto

Wilt Punkte 36662

Datei als Binärinhalt senden (Upload ohne Formular oder FormData)

In den gegebenen Antworten/Beispielen wird die Datei (höchstwahrscheinlich) mit einem HTML-Formular hochgeladen oder mit dem FormData API . Die Datei ist nur ein Teil der in der Anfrage gesendeten Daten, daher die multipart/form-data Content-Type Kopfzeile.

Wenn Sie die Datei als einzigen Inhalt senden möchten, können Sie sie direkt als Body der Anfrage hinzufügen und die Content-Type Header auf den MIME-Typ der zu sendenden Datei. Der Dateiname kann im Feld Content-Disposition Kopfzeile. Sie können wie folgt hochladen:

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);

Wenn Sie keine Formulare verwenden (wollen) und nur eine einzige Datei hochladen möchten, ist dies der einfachste Weg, um Ihre Datei in die Anfrage aufzunehmen.

0 Stimmen

Wie konfigurieren Sie einen serverseitigen Dienst für diese mit Asp.Net 4.0? Wird er auch mehrere Eingabeparameter verarbeiten, z. B. userId, path, captionText usw.?

1 Stimmen

@AsleG Nein, es ist nur für das Senden einer einzelnen Datei als Inhalt Ihrer Anfrage. Ich bin kein Asp.Net-Experte, aber Sie sollten einfach den Inhalt (einen Blob) aus der Anfrage ziehen und ihn in einer Datei speichern, indem Sie die Content-Type aus der Kopfzeile.

0 Stimmen

@AsleG Vielleicht dieser Link kann helfen

19voto

Koray Tugay Punkte 21603

Ich habe dieses Beispiel 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);
    }
}

und ich habe diese Datei test.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>

und schließlich die Datei, die ich zu Testzwecken verwenden werde, namens a.dat hat den folgenden Inhalt:

0x39 0x69 0x65

wenn Sie die obigen Bytes als ASCII- oder UTF-8-Zeichen interpretieren, werden sie tatsächlich dargestellt:

9ie

Starten wir also unseren Java-Code, öffnen wir test.html in unserem Lieblingsbrowser, hochladen a.dat und senden Sie das Formular ab, um zu sehen, was unser Server empfängt:

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--

Nun, ich bin nicht überrascht, die Zeichen zu sehen 9ie weil wir Java angewiesen haben, sie als UTF-8-Zeichen zu behandeln. Sie können sie aber auch als rohe Bytes lesen

Cookie: JSESSIONID=27D0A0637A0449CF65B3CB20F40048AF 

ist hier der letzte HTTP-Header. Danach kommt der HTTP-Body, in dem die Metadaten und der Inhalt der Datei, die wir hochgeladen haben, zu sehen sind.

0 Stimmen

Hallo, ist die Zeile while ((readChar = (char) inputStreamReader.read()) != -1) { im obigen Programm richtig? (int)(char)-1 ist eigentlich 65535

0 Stimmen

@Daniele Sollte richtig sein..

7voto

flagg19 Punkte 1750

Eine HTTP-Nachricht kann nach den Kopfzeilen einen Datenteil enthalten. In einer Antwort wird die angeforderte Ressource an den Client zurückgesendet (die häufigste Verwendung des Nachrichtentextes), oder vielleicht ein erklärender Text, wenn ein Fehler aufgetreten ist. Bei einer Anfrage werden hier vom Benutzer eingegebene Daten oder hochgeladene Dateien an den Server gesendet.

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X