[http] HTTP 파일 업로드는 어떻게 작동합니까?

파일이 첨부 된 간단한 양식을 제출하면 :

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

파일을 내부적으로 어떻게 보냅니 까? 파일이 HTTP 본문의 일부로 데이터로 전송됩니까? 이 요청의 헤더에는 파일 이름과 관련된 내용이 없습니다.

파일을 보낼 때 HTTP의 내부 작동을 알고 싶습니다.



답변

파일을 선택하고 양식을 제출할 때 어떤 일이 발생하는지 살펴 보겠습니다 (간단하게 헤더를 자른 것입니다).

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

참고 : --마지막 경계 문자열의 끝에서와 같이 각 경계 문자열에는 접두사가 추가되어야 합니다. 위의 예는 이미 이것을 포함하고 있지만 놓치기 쉽습니다. 아래 @Andreas의 코멘트를 참조하십시오.

양식 매개 변수를 URL 인코딩하는 대신 양식 매개 변수 (파일 데이터 포함)는 요청 본문의 여러 부분으로 된 문서에서 섹션으로 전송됩니다.

위의 예에서는 MAX_FILE_SIZE파일 데이터가 포함 된 섹션뿐만 아니라 양식에 설정된 값으로 입력 을 볼 수 있습니다 . 파일 이름은 Content-Disposition헤더의 일부입니다 .

자세한 내용은 여기에 있습니다 .


답변

파일을 내부적으로 어떻게 보냅니 까?

형식은 호출 multipart/form-data에서 요청과 같은 : 무엇에 enctype = ‘다중 / 폼 데이터’는 뜻입니까?

할거다:

  • HTML5 참조를 더 추가하십시오.
  • 설명 그가 양식을 제출 예와 권리

HTML5 참조

다음과 같은 세 가지 가능성 이 있습니다 enctype.

예제를 생성하는 방법

각 방법의 예를 보면 각 방법의 작동 방식과 사용 방법이 명확 해집니다.

다음을 사용하여 예제를 생성 할 수 있습니다.

양식을 최소 .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>

우리의 기본 텍스트 값 설정 a&#x03C9;b수단 aωb때문 ω이다 U+03C9바이트있는, 61 CF 89 62UTF-8인치

업로드 할 파일을 작성하십시오.

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

작은 에코 서버를 실행하십시오.

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

브라우저에서 HTML을 열고 파일을 선택한 후 제출을 클릭하고 터미널을 확인하십시오.

nc 받은 요청을 인쇄합니다.

테스트 대상 : Ubuntu 14.04.3, ncBSD 1.105, Firefox 40.

멀티 파트 / 폼 데이터

Firefox가 보냈습니다 :

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

이진 파일 및 텍스트 필드의 경우 바이트 61 CF 89 62( aωbUTF-8)가 문자 그대로 전송됩니다. nc -l localhost 8000 | hd를 사용하여 바이트를 확인할 수 있습니다 .

61 CF 89 62

전송되었습니다 ( 61== ‘a’및 62== ‘b’).

따라서 다음 사항이 분명합니다.

  • Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150컨텐츠 유형을로 설정하고 multipart/form-data필드가 주어진 boundary문자열 로 구분된다고 말합니다 .

    그러나 다음 사항에 유의하십시오.

    boundary=---------------------------735323031399963166993862150
    

    --실제 장벽보다 덜 두껍 습니다.

    -----------------------------735323031399963166993862150
    

    표준에 경계가 두 개의 대시로 시작해야하기 때문 --입니다. 다른 대시는 Firefox가 임의의 경계를 구현하기로 선택한 방식으로 보입니다. RFC 7578은 다음 두 개의 대시 --가 필요하다고 분명히 언급 합니다.

    4.1. multipart / form-data의 “경계”매개 변수

    다른 다중 부품 유형과 마찬가지로 부품은 CRLF, “-“및 “boundary”매개 변수의 값을 사용하여 구성된 경계 구분 기호로 구분됩니다.

  • 모든 필드는 데이터 전에 일부 서브 헤더를 가져옵니다 Content-Disposition: form-data;필드 namefilename데이터 다음을.

    서버는 다음 경계 문자열까지 데이터를 읽습니다. 브라우저는 어떤 필드에도 나타나지 않는 경계를 선택해야하므로 요청마다 경계가 다를 수 있습니다.

    고유 경계가 있으므로 데이터 인코딩이 필요하지 않습니다. 이진 데이터는 그대로 전송됩니다.

    TODO : 최적의 경계 크기 ( log(N)내기)와 그것을 찾는 알고리즘의 이름 / 실행 시간은 얼마입니까? 질문 : /cs/39687/find-the-shortest-sequence-that-is-not-a-sub-sequence-of-a-set-of-sequences

  • Content-Type 브라우저에 의해 자동으로 결정됩니다.

    정확하게 결정된 방법 : 업로드 된 파일의 MIME 유형은 브라우저에 의해 어떻게 결정됩니까?

application / x-www-form-urlencoded

이제 변경 enctype에를 application/x-www-form-urlencoded, 브라우저, 그리고 다시 보내기를 다시로드합니다.

Firefox가 보냈습니다 :

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

분명히 파일 데이터는 전송되지 않았고 기본 이름 만 전송되었습니다. 따라서 파일에는 사용할 수 없습니다.

텍스트 필드에 관해서는, 우리는 같은 것을 보통의 인쇄 가능한 문자를 참조 ab같은 인쇄 할 수없는 것들 동안 1 바이트에 보내졌다 0xCF하고 0x89위로했다 3 바이트 각 : %CF%89!

비교

파일 업로드는 종종 인쇄 할 수없는 많은 문자 (예 : 이미지)를 포함하지만 텍스트 형식은 거의 없습니다.

예제에서 우리는 다음을 보았습니다.

  • multipart/form-data: 메시지에 몇 바이트의 경계 오버 헤드를 추가하고 계산하는 데 시간이 걸리지 만 각 바이트를 1 바이트로 보냅니다.

  • application/x-www-form-urlencoded: 필드 당 단일 바이트 경계 ( &)를 갖지만 인쇄 할 수없는 모든 문자에 대해 3 배선형 오버 헤드 계수를 추가합니다 .

따라서로 파일을 보낼 수 있어도 application/x-www-form-urlencoded비효율적이므로 원하지 않습니다.

그러나 텍스트 필드에있는 인쇄 가능한 문자의 경우 중요하지 않으며 오버 헤드가 적으므로 그냥 사용하십시오.


답변

이진 콘텐츠로 파일 보내기 (양식 또는 FormData없이 업로드)

주어진 답변 / 예에서 파일은 HTML 양식으로 또는 FormData API를 사용하여 업로드 될 가능성이 높습니다 . 파일은 요청에서 전송 된 데이터의 일부일 뿐이므로 multipart/form-data Content-Type헤더입니다.

파일을 유일한 컨텐츠로 보내려면 요청 본문으로 직접 추가하고 Content-Type헤더를 전송중인 파일의 MIME 유형으로 설정할 수 있습니다. Content-Disposition헤더 에 파일 이름을 추가 할 수 있습니다 . 다음과 같이 업로드 할 수 있습니다.

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

양식을 사용하지 않으려는 경우 하나의 단일 파일 만 업로드하려는 경우 요청에 파일을 포함시키는 가장 쉬운 방법입니다.


답변

이 샘플 Java 코드가 있습니다.

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

public class TestClass {
    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(8081);
        Socket accept = socket.accept();
        InputStream inputStream = accept.getInputStream();

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

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

마지막으로 테스트 목적으로 사용할 파일 a.dat 라는 이름 은 다음과 같습니다.

0x39 0x69 0x65

위의 바이트를 ASCII 또는 UTF-8 문자로 해석하면 실제로 다음을 나타냅니다.

9ie

Java 코드를 실행하고 즐겨 사용하는 브라우저에서 test.html 을 열고 a.dat양식을 업로드 하고 제출 한 다음 서버가받는 내용을 확인하십시오.

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

Java에 문자를 UTF-8 문자로 처리하도록 인쇄하도록 지시했기 때문에 문자 9 를 보는 것에 놀라지 않습니다 . 원시 바이트로 읽도록 선택할 수도 있습니다.

Cookie: JSESSIONID=27D0A0637A0449CF65B3CB20F40048AF

실제로 마지막 HTTP 헤더입니다. 그 후 우리가 업로드 한 파일의 메타와 내용을 볼 수있는 HTTP 본문이 나온다.


답변

HTTP 메시지에는 헤더 행 다음에 전송 된 데이터 본문이있을 수 있습니다. 이에 대한 응답으로 요청 된 리소스가 클라이언트 (메시지 본문의 가장 일반적인 용도)로 반환되거나 오류가있는 경우 설명 텍스트가 반환됩니다. 요청시 사용자가 입력 한 데이터 또는 업로드 된 파일이 서버로 전송됩니다.

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


답변