Jak działa przesyłanie plików HTTP?

Kiedy składam taki prosty formularz z załączonym plikiem:

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

Jak wysyła plik wewnętrznie? Czy plik jest wysyłany jako część ciała HTTP jako dane? W nagłówkach tego żądania nie widzę nic związanego z nazwą pliku.

Chciałbym tylko znać wewnętrzne działanie HTTP podczas wysyłania pliku.

Author: Mark Amery, 2011-12-28

5 answers

Przyjrzyjmy się, co się stanie, gdy wybierzesz plik i wyślesz formularz (obcięłem nagłówki dla zwięzłości):

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

Zamiast adresu URL kodującego parametry formularza, parametry formularza (w tym dane Pliku) są wysyłane jako sekcje w dokumencie wieloczęściowym w treści żądania.

W powyższym przykładzie można zobaczyć Wejście MAX_FILE_SIZE z wartością ustawioną w formularzu, a także sekcję zawierającą dane Pliku. Nazwa pliku jest częścią Content-Disposition nagłówek.

Pełne szczegóły są tutaj .

 220
Author: toddsundsted,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-24 12:50:23

Jak wysyła plik wewnętrznie?

Format nazywa się multipart/form-data, jak zapytano w: co oznacza enctype= 'multipart/form-data'?

Idę do:

  • dodaj więcej referencji HTML5
  • wyjaśnij dlaczego ma rację z formularzem wyślij przykład

Odniesienia do HTML5

Istnieją trzy możliwości dla enctype:

Jak wygenerować przykłady

Kiedy zobaczysz przykład każdej z metod, staje się oczywiste, jak one działają i kiedy powinieneś z nich korzystać.

Możesz tworzyć przykłady użycie:

  • W tym celu należy skontaktować się z Działem obsługi klienta pod adresem .]}
  • Nie jest to jednak żaden problem.]}

Zapisz formularz do pliku .html:

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

Ustawiamy domyślną wartość tekstu na a&#x03C9;b, co oznacza aωb, ponieważ ω to U+03C9, które są bajtami 61 CF 89 62 w UTF-8.

Tworzenie plików do przesłania:

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

Uruchom nasz mały serwer echo:

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

Otwórz HTML w swojej przeglądarce, wybierz pliki i kliknij Wyślij i sprawdź terminal.

nc drukuje otrzymane żądanie.

Testowane na: Ubuntu 14.04.3, nc BSD 1.105, Firefox 40.

Multipart / form-data

Firefox wysłał:

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

Dla pliku binarnego i pola tekstowego bajty 61 CF 89 62 (aωb w UTF-8) są wysyłane dosłownie. Można to sprawdzić za pomocą nc -l localhost 8000 | hd, co mówi, że bajty:

61 CF 89 62

Zostały wysłane (61 = = " a " i 62 == "b").

Dlatego jest jasne, że:

  • Content-Type: multipart/form-data; boundary=---------------------------9051914041544843365972754266 ustawia typ zawartości na multipart/form-data i mówi, że pola są oddzielone podanym łańcuchem boundary.

  • Każde pole otrzymuje nagłówki podrzędne przed danymi: Content-Disposition: form-data;, pole name, filename, a następnie dane.

    Serwer odczytuje dane do następnego ciągu granicznego. Przeglądarka musi wybrać granicę, która nie pojawi się w żadnym z pól, dlatego granica może różnią się między żądaniami.

    Ponieważ mamy unikalną granicę, kodowanie danych nie jest konieczne: dane binarne są wysyłane tak, jak są.

    Do zrobienia: jaki jest optymalny rozmiar granicy (log(N) obstawiam) i nazwa / czas działania algorytmu, który go znajdzie? Zapytany o: https://cs.stackexchange.com/questions/39687/find-the-shortest-sequence-that-is-not-a-sub-sequence-of-a-set-of-sequences

  • Content-Type jest automatycznie określana przez przeglądarka.

    Jak to jest dokładnie określone zostało zapytane: jak typ MIME przesłanego pliku jest określony przez przeglądarkę?

Application / x-www-form-urlencoded

Teraz zmień enctype na application/x-www-form-urlencoded, przeładuj przeglądarkę i prześlij ponownie.

Firefox wysłał:

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

Najwyraźniej dane pliku nie zostały wysłane, tylko nazwy podstawowe. Więc to nie może być używane dla plików.

Jeśli chodzi o pole tekstowe, widzimy, że zwykle drukowane znaki, takie jak a i b zostały wysłane w jednym bajcie, podczas gdy nie-drukowalne takie jak 0xCF i 0x89 zajmowały po 3 bajty każdy: %CF%89!

Porównanie

Przesyłane pliki często zawierają wiele niedrukowalnych znaków (np. obrazów), podczas gdy formularze tekstowe prawie nigdy tego nie robią.

Z przykładów widzieliśmy, że:

  • multipart/form-data: dodaje kilka bajtów narzutu do wiadomości i musi poświęcić trochę czasu na jej obliczanie, ale wysyła każdy bajt w jednym bajt.

  • application/x-www-form-urlencoded: ma granicę jednego bajtu na pole (&), ale dodaje liniowy współczynnik napowietrzności 3x dla każdego niedrukowalnego znaku.

Dlatego nawet gdybyśmy mogli wysyłać pliki z application/x-www-form-urlencoded, nie chcielibyśmy, ponieważ jest to tak nieefektywne.

Ale dla drukowalnych znaków znalezionych w polach tekstowych, nie ma to znaczenia i generuje mniej narzutu, więc po prostu go używamy.

 181
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-21 15:48:15

Wyślij plik jako zawartość binarną (upload bez form lub FormData)

W podanych odpowiedziach/przykładach plik jest (najprawdopodobniej) przesyłany za pomocą formularza HTML lub za pomocą FormData API. Plik jest tylko częścią danych przesłanych w żądaniu, stąd multipart/form-data Content-Type nagłówek.

Jeśli chcesz wysłać plik jako jedyną zawartość, możesz bezpośrednio dodać go jako treść żądania i ustawić nagłówek Content-Type na typ MIME wysyłanego pliku. Nazwę pliku można dodać w nagłówku Content-Disposition. Możesz przesłać tak:

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

Jeśli nie chcesz (chcesz) korzystać z formularzy i jesteś zainteresowany przesłaniem tylko jednego pliku, jest to najprostszy sposób, aby dołączyć swój plik do zapytania.

 42
Author: Wilt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-11 08:00:35

Mam ten przykładowy kod Javy:

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);
    }
}
I mam ten test.plik 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>

I na koniec plik, którego będę używał do celów testowych, o nazwie a. dat ma następującą zawartość:

0x39 0x69 0x65

Jeśli zinterpretujesz powyższe bajty jako znaki ASCII lub UTF-8, będą one reprezentować:

9ie

Więc uruchom nasz kod Java, otwórz test.html w naszej ulubionej przeglądarce prześlij a.dat i prześlij formularz i zobacz co nasz serwer otrzymuje:

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

Cóż, nie dziwi mnie widok znaków 9ie ponieważ powiedzieliśmy Javie, aby je wydrukowała traktując je jako znaki UTF-8. Równie dobrze możesz odczytać je jako surowe bajty..

Cookie: JSESSIONID=27D0A0637A0449CF65B3CB20F40048AF 

Jest w rzeczywistości ostatnim nagłówkiem HTTP tutaj. Następnie pojawia się ciało HTTP, gdzie meta i zawartość przesłanego pliku faktycznie mogą być widoczne.

 7
Author: Koray Tugay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-26 19:23:16

Wiadomość HTTP może zawierać dane wysyłane po liniach nagłówka. W odpowiedzi jest to miejsce, w którym żądany zasób jest zwracany do klienta( najczęstsze użycie treści wiadomości), lub być może tekst wyjaśniający, jeśli wystąpi błąd. W żądaniu dane wprowadzone przez użytkownika lub przesłane pliki są wysyłane na serwer.

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

 5
Author: flagg19,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-28 18:42:26