¿Cómo se envía el archivo internamente?
Se llama al formato multipart/form-data
, como se pregunta en: ¿Qué significa enctype = 'multipart / form-data'?
Voy a:
- agregue algunas referencias HTML5 más
- explicar por qué tiene razón con un formulario enviar ejemplo
Referencias HTML5
Hay tres posibilidades para enctype
:
¿Cómo generar los ejemplos?
Una vez que vea un ejemplo de cada método, se hace evidente cómo funcionan y cuándo debe usar cada uno.
Puede producir ejemplos usando:
Guarde el formulario en un .html
archivo 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>
Establecemos el valor de texto predeterminado en aωb
, lo que significa aωb
porque ω
es U+03C9
, que son los bytes 61 CF 89 62
en UTF-8.
Crear archivos para cargar:
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
Ejecute nuestro pequeño servidor echo:
while true; do printf '' | nc -l 8000 localhost; done
Abra el HTML en su navegador, seleccione los archivos y haga clic en enviar y verifique el terminal.
nc
imprime la solicitud recibida.
Probado en: Ubuntu 14.04.3, nc
BSD 1.105, Firefox 40.
multipart / form-data
Firefox envió:
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 el archivo binario y el campo de texto, los bytes 61 CF 89 62
( aωb
en UTF-8) se envían literalmente. Puede verificar eso con nc -l localhost 8000 | hd
, que dice que los bytes:
61 CF 89 62
fueron enviados ( 61
== 'a' y 62
== 'b').
Por lo tanto, está claro que:
Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150
establece el tipo de contenido multipart/form-data
y dice que los campos están separados por el dadoboundary
cadena .
Pero tenga en cuenta que el:
boundary=---------------------------735323031399963166993862150
tiene dos papas menos --
que la barrera real
-----------------------------735323031399963166993862150
Esto se debe a que el estándar requiere que el límite comience con dos guiones --
. Los otros guiones parecen ser exactamente cómo Firefox eligió implementar el límite arbitrario. RFC 7578 menciona claramente que --
se requieren esos dos guiones iniciales:
4.1. Parámetro "límite" de datos multiparte / formulario
Al igual que con otros tipos multiparte, las partes se delimitan con un delimitador de límite, construido utilizando CRLF, "-" y el valor del parámetro "límite".
cada campo obtiene algunos subtítulos antes de sus datos: Content-Disposition: form-data;
el campo name
, elfilename
, seguido de los datos.
El servidor lee los datos hasta la siguiente cadena de límite. El navegador debe elegir un límite que no aparecerá en ninguno de los campos, por lo que el límite puede variar entre las solicitudes.
Debido a que tenemos el límite único, no es necesaria la codificación de los datos: los datos binarios se envían tal cual.
TODO: ¿cuál es el tamaño de límite óptimo ( log(N)
apuesto) y el nombre / tiempo de ejecución del algoritmo que lo encuentra? Preguntado en: /cs/39687/find-the-shortest-sequence-that-is-not-a-sub-sequence-of-a-set-of-sequences
Content-Type
se determina automáticamente por el navegador.
Se preguntó cómo se determina exactamente en: ¿Cómo se determina el tipo mime de un archivo cargado por el navegador?
application / x-www-form-urlencoded
Ahora cambie enctype
a application/x-www-form-urlencoded
, vuelva a cargar el navegador y vuelva a enviar.
Firefox envió:
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, los datos del archivo no se enviaron, solo los nombres básicos. Por lo tanto, esto no se puede usar para archivos.
En cuanto al campo de texto, vemos que a los caracteres imprimibles habituales les gusta a
y b
se enviaron en un byte, mientras que a los no imprimibles les gusta 0xCF
y 0x89
ocupaban 3 bytes cada uno %CF%89
:!
Comparación
Las cargas de archivos a menudo contienen muchos caracteres no imprimibles (por ejemplo, imágenes), mientras que los formularios de texto casi nunca lo hacen.
De los ejemplos hemos visto que:
multipart/form-data
: agrega unos pocos bytes de sobrecarga de límite al mensaje, y debe pasar algún tiempo calculándolo, pero envía cada byte en un byte.
application/x-www-form-urlencoded
: tiene un límite de un solo byte por campo ( &
), pero agrega un factor de sobrecarga lineal de 3x por cada carácter no imprimible.
Por lo tanto, incluso si pudiéramos enviar archivos con application/x-www-form-urlencoded
, no querríamos hacerlo, porque es muy ineficiente.
Pero para los caracteres imprimibles que se encuentran en los campos de texto, no importa y genera menos sobrecarga, por lo que solo lo usamos.