重拾cgi——檔案上傳和cgicc

shy丶gril發表於2016-03-24

html中上傳檔案,只需要表單裡面放一個input type=file即可,如果要使用ajax非同步上傳(下文基於jquery),就需要注意幾點(以下操作,部分需要基於html5定義的api):

1、頁面上建立一個input元素,type是file。如果一個input需要支援選擇多個檔案,在標籤中增加屬性:multiple=”multiple”。(注意,這需要瀏覽器支援html5,具體文件可以見

2、獲取這個元素繫結的已上傳檔案
[cce lang=”javascript”]
var files = document.getElementById(‘xxx’).files;
[/cce]
先獲取元素,通過讀取元素的files屬性,獲取所有已經選擇的檔案。

3、組裝formdata,因為上傳檔案可能會比較大,所以即使支援多選檔案,這裡還是分成不同的請求傳送。
[cce lang=”javascript”]
for(var i=0;i<array.length;i++) {
var file = files[index];
var formData = new FormData();
formData.append(“file”, file);
formData.append(“id”, i+1);
}
[/cce]
這裡將每個檔案單獨組裝成一個formdata,裡面包含檔案內容和一個id。

4、通過jquery發起post請求:
[cce lang=”javascript”]
$.ajax({
type: `POST`,
url: “file/new”,
data: formData,
async: true,
cache: false,
processData: false,
contentType: false,
xhr: function() {
myXhr = $.ajaxSettings.xhr();
if(myXhr.upload){
myXhr.upload.addEventListener(`progress`,progressHandlerFunction, false);
}
return myXhr;
},
success: function(data){
if(!data.result) {
progressTag.remove();
$(“#” + index).after($(`<span style=”color:red”>`+data.msg+`</span>`));
}
}
});
[/cce]
其中的xhr部分稍後會提到,這裡還有兩個需要特別注意的地方。首先是processData屬性,必須設定為false,其次是contentType必須設定為false。其他後端不清楚,cgicc在處理檔案上傳(既multipart方式post的時候),必須從http頭中讀取Content-Disposition屬性,只有在jquery裡面設定了前面提到的兩個屬性,才能正確的按照標準協議進行封裝,讓cgicc讀取到正確的內容分隔字串。

5、錦上添花,增加進度條
[cce lang=”javascript”]
var progressTag = $(“<progress/>”, {
value: 0,
min: 0,
max: file.size
});
$(“#” + index).after(progressTag);

function progressHandlerFunction(evt) {
if (evt.lengthComputable) {
progressTag.attr(“max”, evt.total);
progressTag.attr(“value”, evt.loaded);
}
}
[/cce]
這裡通過jquery動態的增加了一個progress標籤(html5中新增),然後定義了一個handler。之前jquery的ajax函式中看見了,通過獲取原生xhr物件,在upload事件上增加handler函式回撥,在上傳的同時,將具體進度反饋給使用者。當然,如果需要,這裡也可以繫結download事件。

6、cgicc處理檔案
cgicc處理檔案和普通表單元素類似,唯一的區別是需要通過cgi.getFile函式來獲取,而不是cgi.getElement。
[cce lang=”cpp”]
cgicc::form_iterator idIter = cgi.getElement(“id”);
cgicc::file_iterator fileIter = cgi.getFile(“file”);
if(idIter == cgi.getElements().end())
{
//handle error
}
int id = (int)idIter->getIntegerValue();

if(fileIter == cgi.getFiles().end())
{
//handle error
}
std::ofstream of(boost::lexical_cast<std::string>(id));
fileIter->writeToStream(of);
[/cce]
這裡忽略了錯誤處理,通過獲取表單元素,作為檔名,直接將檔案通過標準庫檔案輸出流ofstream寫到磁碟上。

這樣,就完成了從頁面上選擇檔案,到後臺儲存的簡單流程。


相關文章