respuesta protocolo para headers español enviar encabezados dummies cabeceras http file-upload

protocolo - php cabeceras http



¿Cómo funciona la carga de archivos HTTP? (5)

¿Cómo se envía el archivo internamente?

El formato se llama multipart/form-data , como se pregunta en: ¿Qué significa enctype = ''multipart / form-data''?

Voy a:

  • añadir algunas referencias HTML5 más
  • explique 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 vuelve obvio cómo funcionan y cuándo debe usar cada uno.

Puedes producir ejemplos usando:

Guarde el formulario en un archivo .html 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&#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>

Establecemos el valor de texto predeterminado a a&#x03C9;b , lo que significa aωb porque ω es U+03C9 , que son los bytes 61 CF 89 62 en UTF-8.

Crea archivos para subir:

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 de eco:

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. Podrías verificar que con nc -l localhost 8000 | hd 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=---------------------------9051914041544843365972754266 Content-Type: multipart/form-data; boundary=---------------------------9051914041544843365972754266 establece el tipo de contenido en multipart/form-data y dice que los campos están separados por el dado cadena de boundary .

  • cada campo obtiene algunos subencabezados antes de sus datos: Content-Disposition: form-data; , el name del campo, el filename del filename , 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 solicitudes.

    Debido a que tenemos un límite único, no es necesaria la codificación de los datos: los datos binarios se envían tal como están.

    TODO: ¿cuál es el tamaño de límite óptimo ( log(N) que apuesto) y el nombre / tiempo de ejecución del algoritmo que lo encuentra? Consultado en: https://cs.stackexchange.com/questions/39687/find-the-shortest-sequence-that-is-not-a-sub-sequence-of-a-set-of-sequences

  • Content-Type es determinado automáticamente por el navegador.

    ¿Cómo se determina exactamente cómo se preguntó? ¿Cómo se determina el tipo de mime de un archivo cargado por el navegador?

aplicación / x-www-form-urlencoded

Ahora cambie el 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 de base. Así que esto no puede ser usado para archivos.

En cuanto al campo de texto, vemos que los caracteres imprimibles habituales como a y b se enviaron en un byte, mientras que los no imprimibles como 0xCF y 0x89 tomaron 3 bytes cada uno: %CF%89 !

Comparación

La carga de archivos a menudo contiene muchos caracteres no imprimibles (por ejemplo, imágenes), mientras que las formas 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 en calcularlo, 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 application/x-www-form-urlencoded 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 simplemente lo usamos.

Cuando envío un formulario simple como este con un archivo adjunto:

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

¿Cómo se envía el archivo internamente? ¿Se envía el archivo como parte del cuerpo HTTP como datos? En los encabezados de esta solicitud, no veo nada relacionado con el nombre del archivo.

Solo me gustaría saber el funcionamiento interno de HTTP al enviar un archivo.


Un mensaje HTTP puede tener un cuerpo de datos enviados después de las líneas del encabezado. En una respuesta, aquí es donde el recurso solicitado se devuelve al cliente (el uso más común del cuerpo del mensaje), o quizás un texto explicativo si hay un error. En una solicitud, aquí es donde los datos ingresados ​​por el usuario o los archivos cargados se envían al servidor.

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


Enviar archivo como contenido binario (subir sin formulario o FormData)

En las respuestas / ejemplos dados, el archivo es (lo más probable) subido con un formulario HTML o utilizando la API FormData . El archivo es solo una parte de los datos enviados en la solicitud, de ahí el encabezado de Content-Type multipart/form-data .

Si desea enviar el archivo como el único contenido, puede agregarlo directamente como el cuerpo de la solicitud y configurar el encabezado Content-Type al tipo MIME del archivo que está enviando. El nombre del archivo se puede agregar en el encabezado Content-Disposition . Puedes subir como esta:

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

Si no (quiere) usar formularios y solo le interesa cargar un solo archivo, esta es la forma más fácil de incluir su archivo en la solicitud.


Echemos un vistazo a lo que sucede cuando selecciona un archivo y envía su formulario (he truncado los encabezados para mayor brevedad):

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

En lugar de que la URL codifique los parámetros del formulario, los parámetros del formulario (incluidos los datos del archivo) se envían como secciones en un documento de varias partes en el cuerpo de la solicitud.

En el ejemplo anterior, puede ver la entrada MAX_FILE_SIZE con el valor establecido en el formulario, así como una sección que contiene los datos del archivo. El nombre del archivo es parte del encabezado Content-Disposition .

Los detalles completos están here .


Tengo este código Java de muestra:

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

y tengo este archivo 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>

y, finalmente, el archivo que usaré con fines de prueba, denominado a.dat, tiene el siguiente contenido:

0x39 0x69 0x65

Si interpreta los bytes anteriores como caracteres ASCII o UTF-8, en realidad representarán:

9ie

Así que ejecutemos nuestro código Java, abra test.html en nuestro navegador favorito, cargue a.dat y envíe el formulario para ver qué recibe nuestro servidor:

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

Bueno, no me sorprende ver los personajes 9ie porque le dijimos a Java que los imprimiera tratándolos como caracteres UTF-8. Puede elegir leerlos como bytes sin procesar.

Cookie: JSESSIONID=27D0A0637A0449CF65B3CB20F40048AF

Es en realidad el último encabezado HTTP aquí. Después de eso viene el Cuerpo HTTP, donde se pueden ver los metadatos y el contenido del archivo que cargamos.