模擬HTML表單上傳檔案(RFC 1867)

2021-06-21 00:25:50 字數 4740 閱讀 1566

如今使用http協議定製api已經是十分常見的事情,在普通的get和post請求中傳遞些引數估計人人都會,但是如果我們需要上傳檔案呢?如果只是傳遞單個檔案,那麼將資料流post給伺服器端即可。但如果需要上傳多個檔案,或是在檔案之外需要附帶一些資訊,那麼又該怎麼做呢?之前我遇到過一些朋友是這麼打算的,他們說,不如就把檔案流轉化為文字,然後把它當作乙個普通的字段傳遞。這麼做自然可以「實現功能」,但缺點也很多。首先,將二進位製流轉化為文字會增大體積(例如最常見的base64編碼會增大1/3的資料量);其次,既然網際網路上存在相關的協議,又為何要自定義一套規則呢?其實這便是《rfc 1867 - form-based file upload in html》,它是我們用html表單上傳檔案時使用的傳輸協議,雖然十分常用,但似乎了解它的人並不多。

mytext1=hello+world&mytext2=%e4%bd%a0%e5%a5%bd%e4%b8%96%e7%95%8c不過之前的html表單是無法上傳檔案的,因此rfc 1867應運而生,它的目的便是讓html表單可以提交檔案。它對html表單的擴充套件主要是:

於是,如果我們要使用html表單提交檔案,則可以使用如下定義:

<

form

action

=""

method

="post"

enctype

="multipart/form-data">

<

input

type

="text"

name

="mytext" /><

br />

<

input

type

="file"

name

="upload1" /><

br />

<

input

type

="file"

name

="upload2" /><

br />

<

input

type

="submit" />

form

>

為了實驗所需,我們建立兩個檔案file1.txt和file2.txt,內容分別為「this is file1.」及「this is file2, it's bigger.」。在文字框裡寫上「hello world」,並選擇這兩個檔案,提交,則會看到瀏覽器傳遞了如下資料:

-----------------------------7db2d1bcc50e6e--這段內容比較有趣,值得細細觀察。首先,第乙個空行之前自然還是http頭,之後則是body,而此時的body也比之前要複雜一些。根據rfc 1867定義,我們需要選擇一段資料作為「分割邊界」,這個「邊界資料」不能在內容其他地方出現,一般來說使用一段從概率上說「幾乎不可能」的資料即可。例如,上面這段資料使用的是ie 9,而我在chrome下則是這樣的:

------webkitformboundaryw49oa00lu29e4c5u--很顯然它們兩個選擇了不同的資料「模式」作為邊界——事實上,瀏覽器提交兩次資料時,使用的邊界也可能不會相同,這都沒有問題。

選擇了邊界之後,便會將它放在頭部的content-type裡傳遞給伺服器端,實際需要傳遞的資料便可以分割為「段」,每段便是「一項」資料。從上面的內容中大家應該都能看出資料傳輸的規範,因此便不做細談了。只強調幾點:

了解上述策略之後,使用程式設計來實現檔案上傳也是順理成章的事情,例如我這裡便編寫了一段簡單的**實現這一功能。

首先,我們定義乙個part類,表示每「段」,它的write方法會寫入整段資料。每段資料分為header和body兩部分,使用writeheader和writebody兩個抽象方法寫入:

public abstract class 

part

}

接著便是表示普通欄位的normalpart和檔案上傳得filepart:

public class 

normalpart : part

public string value

protected override void writeheader(streamwriter writer)

\"", this.name);

}protected override void writebody(streamwriter writer)

}public class

filepart : part

public string filepath

protected override void writeheader(streamwriter writer)

\"; filename=\"\"",

this.name,

path.getfilename(this.filepath));

}protected override void writebody(streamwriter writer)

}

最後便是統一寫入各段的write方法,我在這裡使用新建的guid作為「邊界」:

static void write(streamwriter writer, ienumerable

parts)

writer.writeline(boundary + "--");

}

其實就是這麼簡單。不過在實際情況中可能會複雜一些。例如,由於http協議需要先傳送頭資訊,因此我們需要提前計算出content-length再傳輸所有內容,不過我相信這對您來說也不會是件難事。

世界上已經有了足夠多的協議,在我看來在絕大部分情況下都無所謂使用自定義的協議。協議在制定時,往往也會考慮到安全、效能等諸多方面,有時候我們自己所謂的「顧慮」其理由也並不充分。更重要的是,使用現成的協議,我們往往都有現成的實現,對於開發和測試都會有很大幫助。

rfc 1867是乙個很簡單的協議,當然再簡單也不是我這短短一篇文章可以完整描述的,其中很多細節(例如在同乙個「段」中上傳多個檔案)就要靠您自己去挖掘了。

表單上傳檔案相關

在上提交表單之前對上傳檔案進行校驗 button add click function 對上傳檔案的格式進行校驗 var filetype file.value.substr file.value.lastindexof 1 if doc filetype pdf filetype else ret...

form表單上傳檔案

views def up request return render request,up.html html 這時候隨便選擇乙個檔案傳送,結果當然是不成功的。錯誤資訊 csrf verification failed.request aborted.csrf驗證失敗。請求中止。不能發,現在要朝著後...

使用form表單上傳檔案

在使用form表單上傳檔案時候,input type file 是必然會用的,其中有一些小坑需要避免。1 form的 enctype multipart form data 已經是個老生常談的問題了,相信都能注意到,就不多說了。3 當使用input type file 的onchange事件來觸發檔...