Como ele envia o arquivo internamente?
O formato é chamado multipart/form-data
, conforme solicitado em: O que significa enctype = 'multipart / form-data'?
Eu vou:
- adicione mais algumas referências HTML5
- explicar por que ele está certo com um exemplo de envio de formulário
Referências HTML5
Existem três possibilidades para enctype
:
Como gerar os exemplos
Depois de ver um exemplo de cada método, fica óbvio como eles funcionam e quando você deve usar cada um.
Você pode produzir exemplos usando:
Salve o formulário em um .html
arquivo mínimo :
<!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>
Definimos o valor de texto padrão como aωb
, o que significa aωb
porque ω
é U+03C9
, quais são os bytes 61 CF 89 62
em UTF-8.
Crie arquivos para upload:
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
Execute nosso pequeno servidor de eco:
while true; do printf '' | nc -l 8000 localhost; done
Abra o HTML no seu navegador, selecione os arquivos, clique em enviar e verifique o terminal.
nc
imprime a solicitação recebida.
Testado em: Ubuntu 14.04.3, nc
BSD 1.105, Firefox 40.
multipart / form-data
Firefox enviado:
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--
Para o arquivo binário e o campo de texto, os bytes 61 CF 89 62
( aωb
em UTF-8) são enviados literalmente. Você pode verificar isso com nc -l localhost 8000 | hd
, que diz que os bytes:
61 CF 89 62
foram enviados ( 61
== 'a' e 62
== 'b').
Portanto, é claro que:
Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150
define o tipo de conteúdo como multipart/form-data
e diz que os campos são separados pela boundary
sequência especificada .
Mas observe que o:
boundary=---------------------------735323031399963166993862150
tem dois paizinhos a menos do --
que a barreira real
-----------------------------735323031399963166993862150
Isso ocorre porque o padrão requer que o limite comece com dois traços --
. Os outros traços parecem ser exatamente como o Firefox escolheu implementar o limite arbitrário. A RFC 7578 menciona claramente que esses dois traços principais --
são necessários:
4.1 Parâmetro "Limite" de dados de várias partes / formulário
Como com outros tipos de várias partes, as partes são delimitadas com um delimitador de limite, construído usando CRLF, "-" e o valor do parâmetro "limite".
todo campo recebe alguns subtítulos antes de seus dados:, Content-Disposition: form-data;
o campo name
, o filename
, seguido pelos dados.
O servidor lê os dados até a próxima sequência de limites. O navegador deve escolher um limite que não apareça em nenhum dos campos; é por isso que o limite pode variar entre solicitações.
Como temos um limite único, nenhuma codificação dos dados é necessária: dados binários são enviados como estão.
TODO: qual é o tamanho ideal do limite ( log(N)
aposto) e o nome / tempo de execução do algoritmo que o encontra? Perguntado em: /cs/39687/find-the-shortest-sequence-that-is-not-a-sub-sequence-of-a-set-of-sequences
Content-Type
é determinado automaticamente pelo navegador.
Como foi determinado exatamente foi perguntado em: Como o tipo MIME de um arquivo carregado é determinado pelo navegador?
application / x-www-form-urlencoded
Agora mude enctype
para application/x-www-form-urlencoded
, recarregue o navegador e reenvie.
Firefox enviado:
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
Claramente, os dados do arquivo não foram enviados, apenas os nomes de base. Portanto, isso não pode ser usado para arquivos.
Como para o campo de texto, vemos que caracteres imprimíveis habituais, como a
e b
foram enviados em um byte, enquanto os não-imprimíveis, como 0xCF
e 0x89
pegou 3 bytes cada um: %CF%89
!
Comparação
O upload de arquivos geralmente contém muitos caracteres não imprimíveis (por exemplo, imagens), enquanto os formulários de texto quase nunca o fazem.
A partir dos exemplos, vimos que:
multipart/form-data
: adiciona alguns bytes de sobrecarga de limite à mensagem e deve passar algum tempo calculando-a, mas envia cada byte em um byte.
application/x-www-form-urlencoded
: possui um limite de byte único por campo ( &
), mas adiciona um fator de sobrecarga linear de 3x para cada caractere não imprimível.
Portanto, mesmo se pudéssemos enviar arquivos application/x-www-form-urlencoded
, não desejaríamos, porque é muito ineficiente.
Porém, para caracteres imprimíveis encontrados em campos de texto, isso não importa e gera menos sobrecarga; portanto, apenas o usamos.