Làm thế nào để nó gửi các tập tin nội bộ?
Định dạng được gọi multipart/form-data
, như được hỏi tại: enctype = 'Multipart / form-data' nghĩa là gì?
Tôi sẽ:
- thêm một số tài liệu tham khảo HTML5
- giải thích lý do tại sao anh ta đúng với một ví dụ gửi mẫu
Tài liệu tham khảo HTML5
Có ba khả năng cho enctype
:
Cách tạo các ví dụ
Khi bạn thấy một ví dụ về mỗi phương thức, nó sẽ trở nên rõ ràng về cách chúng hoạt động và khi nào bạn nên sử dụng từng phương thức.
Bạn có thể tạo các ví dụ bằng cách sử dụng:
Lưu biểu mẫu vào một .html
tệp tối thiểu :
<!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>
Chúng tôi thiết lập các giá trị văn bản mặc định aωb
, mà phương tiện aωb
vì ω
là U+03C9
, đó là các byte 61 CF 89 62
trong UTF-8.
Tạo tập tin để tải lên:
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
Chạy máy chủ echo nhỏ của chúng tôi:
while true; do printf '' | nc -l 8000 localhost; done
Mở HTML trên trình duyệt của bạn, chọn các tệp và nhấp vào gửi và kiểm tra thiết bị đầu cuối.
nc
in yêu cầu nhận được.
Đã thử nghiệm trên: Ubuntu 14.04.3, nc
BSD 1.105, Firefox 40.
nhiều dữ liệu / biểu mẫu
Firefox đã gửi:
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--
Đối với tệp nhị phân và trường văn bản, các byte 61 CF 89 62
( aωb
trong UTF-8) được gửi theo nghĩa đen. Bạn có thể xác minh rằng với nc -l localhost 8000 | hd
, nói rằng các byte:
61 CF 89 62
đã được gửi ( 61
== 'a' và 62
== 'b').
Vì vậy, rõ ràng rằng:
Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150
đặt loại nội dung thành multipart/form-data
và nói rằng các trường được phân tách bằng boundary
chuỗi đã cho .
Nhưng lưu ý rằng:
boundary=---------------------------735323031399963166993862150
có hai cha ít --
hơn rào cản thực tế
-----------------------------735323031399963166993862150
Điều này là do tiêu chuẩn yêu cầu ranh giới bắt đầu bằng hai dấu gạch ngang --
. Các dấu gạch ngang khác dường như là cách Firefox chọn để thực hiện ranh giới tùy ý. RFC 7578 đề cập rõ ràng rằng hai dấu gạch ngang hàng đầu đó --
là bắt buộc:
4.1. "Ranh giới" Tham số của nhiều dữ liệu / biểu mẫu
Cũng như các loại nhiều phần khác, các phần được phân định bằng dấu phân cách ranh giới, được xây dựng bằng CRLF, "-" và giá trị của tham số "ranh giới".
mỗi trường có một số tiêu đề phụ trước dữ liệu của nó : Content-Disposition: form-data;
, trường name
, the filename
, theo sau là dữ liệu.
Máy chủ đọc dữ liệu cho đến chuỗi ranh giới tiếp theo. Trình duyệt phải chọn một ranh giới sẽ không xuất hiện trong bất kỳ trường nào, vì vậy đây là lý do tại sao ranh giới có thể khác nhau giữa các yêu cầu.
Bởi vì chúng tôi có ranh giới duy nhất, không cần mã hóa dữ liệu: dữ liệu nhị phân được gửi như hiện có.
TODO: kích thước ranh giới tối ưu ( log(N)
tôi đặt cược) và tên / thời gian chạy của thuật toán tìm thấy nó là gì? Đã hỏi tại: /cs/39487/find-the-shortest- resultence-that-is-not-a-sub-resultence-of-a-set-of- result
Content-Type
được tự động xác định bởi trình duyệt.
Làm thế nào nó được xác định chính xác đã được hỏi tại: Làm thế nào loại mime của một tệp tải lên được xác định bởi trình duyệt?
application / x-www-form-urlencoding
Bây giờ thay đổi enctype
thành application/x-www-form-urlencoded
, tải lại trình duyệt và gửi lại.
Firefox đã gửi:
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
Rõ ràng dữ liệu tệp không được gửi, chỉ có các tên cơ sở. Vì vậy, điều này không thể được sử dụng cho các tập tin.
Đối với trường văn bản, chúng tôi thấy rằng các ký tự có thể in thông thường như a
và b
được gửi trong một byte, trong khi các ký tự không in được như thế 0xCF
và 0x89
chiếm 3 byte mỗi ký tự : %CF%89
!
So sánh
Tải lên tệp thường chứa nhiều ký tự không in được (ví dụ hình ảnh), trong khi các hình thức văn bản hầu như không bao giờ thực hiện.
Từ các ví dụ chúng ta đã thấy rằng:
multipart/form-data
: thêm một vài byte chi phí biên cho thông báo và phải dành thời gian để tính toán nó, nhưng gửi mỗi byte trong một byte.
application/x-www-form-urlencoded
: có một ranh giới byte đơn trên mỗi trường ( &
), nhưng thêm hệ số chi phí tuyến tính là 3x cho mỗi ký tự không in được.
Do đó, ngay cả khi chúng tôi có thể gửi tệp cùng application/x-www-form-urlencoded
, chúng tôi sẽ không muốn, vì nó rất kém hiệu quả.
Nhưng đối với các ký tự có thể in được tìm thấy trong các trường văn bản, nó không quan trọng và tạo ra ít chi phí hơn, vì vậy chúng tôi chỉ sử dụng nó.