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.
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 .
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
:
x-www-urlencoded
-
multipart/form-data
(spec wskazuje na RFC2388 ) -
text-plain
. Nie jest to "niezawodnie interpretowane przez komputer", więc nigdy nie powinno być używane w produkcji i nie będziemy się nad tym dalej przyglądać.
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ω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ω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 namultipart/form-data
i mówi, że pola są oddzielone podanym łańcuchemboundary
.-
Każde pole otrzymuje nagłówki podrzędne przed danymi:
Content-Disposition: form-data;
, polename
,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.
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.
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.
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.
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