HTTP फ़ाइल कैसे अपलोड करता है?




file-upload (4)

जब मैं इस तरह एक फ़ाइल के साथ एक साधारण रूप प्रस्तुत करता हूं:

<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 की आंतरिक कार्यवाही जानना चाहूंगा।


एक HTTP संदेश में हेडर लाइनों के बाद भेजे गए डेटा का एक बॉडी हो सकता है। एक प्रतिक्रिया में, यह वह जगह है जहां अनुरोध किया गया संसाधन ग्राहक को वापस भेजा जाता है (संदेश निकाय का सबसे आम उपयोग), या यदि कोई त्रुटि हो तो शायद व्याख्यात्मक पाठ। एक अनुरोध में, यह वह जगह है जहां उपयोगकर्ता द्वारा दर्ज डेटा या अपलोड की गई फाइलें सर्वर पर भेजी जाती हैं।

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


यह फ़ाइल को आंतरिक रूप से कैसे भेजता है?

प्रारूप को multipart/form-data कहा जाता है, जैसा कि पूछा गया: enctype = 'multipart / form-data' का अर्थ क्या है?

मैं जा रहा हूँ:

  • कुछ और एचटीएमएल 5 संदर्भ जोड़ें
  • समझाओ कि वह एक फॉर्म के साथ सही क्यों है उदाहरण सबमिट करें

एचटीएमएल 5 संदर्भ

enctype लिए तीन संभावनाएं हैं:

  • x-www-urlencoded
  • multipart/form-data ( RFC2388 लिए RFC2388 )
  • text-plain यह "कंप्यूटर द्वारा विश्वसनीय रूप से व्याख्या योग्य नहीं है", इसलिए इसे कभी भी उत्पादन में उपयोग नहीं किया जाना चाहिए, और हम इसमें आगे नहीं देखेंगे।

उदाहरण कैसे उत्पन्न करें

एक बार जब आप प्रत्येक विधि का एक उदाहरण देखते हैं, तो यह स्पष्ट हो जाता है कि वे कैसे काम करते हैं, और जब आपको प्रत्येक का उपयोग करना चाहिए।

आप उदाहरणों का उपयोग कर सकते हैं:

  • nc -l या एक ईसीएचओ सर्वर
  • एक ब्राउज़र एजेंट या ब्राउज़र जैसे उपयोगकर्ता एजेंट

फ़ॉर्म को न्यूनतम .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 , जो यूटीएफ -8 में बाइट्स 61 CF 89 62 है।

अपलोड करने के लिए फाइलें बनाएं:

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

अपने ब्राउज़र पर एचटीएमएल खोलें, फाइलों का चयन करें और सबमिट पर क्लिक करें और टर्मिनल की जांच करें।

nc प्राप्त अनुरोध मुद्रित करता है।

पर परीक्षण: उबंटू 14.04.3, nc बीएसडी 1.105, फ़ायरफ़ॉक्स 40।

बहुखण्डीय / फार्म-डेटा

फ़ायरफ़ॉक्स भेजा गया:

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 (यूटीएफ -8 में aωb ) सचमुच भेजे जाते हैं। आप nc -l localhost 8000 | hd साथ सत्यापित कर सकते हैं nc -l localhost 8000 | hd , जो कहता है कि बाइट्स:

61 CF 89 62

भेजे गए थे ( 61 == 'ए' और 62 == 'बी')।

इसलिए यह स्पष्ट है कि:

  • Content-Type: multipart/form-data; boundary=---------------------------9051914041544843365972754266 Content-Type: multipart/form-data; boundary=---------------------------9051914041544843365972754266 सामग्री प्रकार को multipart/form-data सेट करता है और कहता है कि फ़ील्ड दिए गए हैं boundary स्ट्रिंग।

  • प्रत्येक फ़ील्ड को इसके डेटा से पहले कुछ उप-शीर्षलेख मिलते हैं: Content-Disposition: form-data; , क्षेत्र का name , filename , डेटा के बाद।

    सर्वर अगली सीमा स्ट्रिंग तक डेटा पढ़ता है। ब्राउज़र को ऐसी सीमा चुननी होगी जो किसी भी फ़ील्ड में दिखाई न दे, इसलिए यही कारण है कि सीमा अनुरोधों के बीच भिन्न हो सकती है।

    चूंकि हमारे पास अद्वितीय सीमा है, इसलिए डेटा का कोई एन्कोडिंग आवश्यक नहीं है: बाइनरी डेटा भेजा जाता है।

    TODO: इष्टतम सीमा आकार ( log(N) मैं शर्त क्या है), और एल्गोरिदम का नाम / चलने का समय जो इसे पाता है? यहां पूछा गया: https://cs.stackexchange.com/questions/39687/find-the-shortest-sequence-that-is-not-a-sub-sequence-of-a-set-of-sequences

  • Content-Type स्वचालित रूप से ब्राउज़र द्वारा निर्धारित किया जाता है।

    यह कैसे निर्धारित किया जाता है कि बिल्कुल पूछा गया था: ब्राउज़र द्वारा निर्धारित अपलोड की गई फ़ाइल का माइम प्रकार कैसा है?

आवेदन / x-www फार्म-urlencoded

अब enctype को application/x-www-form-urlencoded बदलें, ब्राउज़र को फिर से लोड करें, और पुनः सबमिट करें।

फ़ायरफ़ॉक्स भेजा गया:

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

स्पष्ट रूप से फ़ाइल डेटा नहीं भेजा गया था, केवल बेसनाम। तो यह फ़ाइलों के लिए इस्तेमाल नहीं किया जा सकता है।

टेक्स्ट फ़ील्ड के लिए, हम देखते हैं कि a और b जैसे सामान्य प्रिंट करने योग्य वर्ण एक बाइट में भेजे गए थे, जबकि 0xCF और 0x89 जैसे गैर-प्रिंट करने योग्य वाले प्रत्येक 3 बाइट्स लेते थे: %CF%89 !

तुलना

फ़ाइल अपलोड में अक्सर बहुत सारे गैर-प्रिंट करने योग्य वर्ण होते हैं (उदाहरण के लिए छवियां), जबकि पाठ रूप लगभग कभी नहीं करते हैं।

उदाहरणों से हमने देखा है कि:

  • multipart/form-data : संदेश पर सीमा ओवरहेड के कुछ बाइट जोड़ता है, और इसे गणना करने में कुछ समय बिताना चाहिए, लेकिन प्रत्येक बाइट को एक बाइट में भेजता है।

  • application/x-www-form-urlencoded : प्रति फ़ील्ड ( & ) में एक बाइट सीमा है, लेकिन प्रत्येक गैर-प्रिंट करने योग्य वर्ण के लिए 3x का रैखिक ओवरहेड कारक जोड़ता है।

इसलिए, भले ही हम application/x-www-form-urlencoded साथ फाइलें भेज सकें, हम नहीं चाहते हैं, क्योंकि यह इतना अक्षम है।

लेकिन पाठ क्षेत्रों में पाए जाने वाले प्रिंट करने योग्य पात्रों के लिए, इससे कोई फर्क नहीं पड़ता और कम ओवरहेड उत्पन्न करता है, इसलिए हम इसका उपयोग करते हैं।


आइए देखें कि जब आप कोई फ़ाइल चुनते हैं और अपना फॉर्म जमा करते हैं तो क्या होता है (मैंने हेडर्स को ब्रेवटी के लिए छोटा कर दिया है):

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

फॉर्म पैरामीटर को यूआरएल एन्कोड करने के बजाय, फ़ॉर्म पैरामीटर (फाइल डेटा समेत) अनुरोध के बॉडी में मल्टीपार्ट दस्तावेज़ में सेक्शन के रूप में भेजे जाते हैं।

उपर्युक्त उदाहरण में, आप इनपुट MAX_FILE_SIZE को फॉर्म में मान सेट के साथ-साथ फ़ाइल डेटा वाले अनुभाग के साथ इनपुट देख सकते हैं। फ़ाइल का नाम Content-Disposition शीर्षलेख का हिस्सा है।

पूरा विवरण here


मेरे पास यह नमूना जावा कोड है:

<!-- language: java -->

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

और मेरे पास यह test.html फ़ाइल है:

<!-- language: 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

यदि आप एएससीआईआई या यूटीएफ -8 अक्षरों के रूप में उपरोक्त बाइट्स की व्याख्या करते हैं, तो वे वास्तव में प्रतिनिधित्व करेंगे:

9ie

तो चलिए अपना जावा कोड चलाएं, test.html में 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--

खैर मैं 9ई अक्षरों को देखकर आश्चर्यचकित नहीं हूं क्योंकि हमने उन्हें जावा को यूटीएफ -8 अक्षरों के रूप में उनका इलाज करने के लिए कहा था। आप कच्चे बाइट्स के रूप में उन्हें पढ़ने का भी चयन कर सकते हैं ..

Cookie: JSESSIONID=27D0A0637A0449CF65B3CB20F40048AF 

वास्तव में यहां अंतिम HTTP शीर्षलेख है। उसके बाद HTTP बॉडी आता है, जहां हमने अपलोड की गई फ़ाइल की मेटा और सामग्री वास्तव में देखी जा सकती है।







file-upload