您當前的位置:首頁 > 收藏

說清楚檔案上傳

作者:由 危險 發表于 收藏時間:2018-03-23

說清楚檔案上傳

dir:接收上傳內容的目錄

public:存放前端測試頁面的目錄

依賴:用到了express和formidable,分別用來搭建本地服務和提供/upload上傳介面

版本1:

對應HTML程式碼

<!——

action=“/upload”:指定後端上傳介面

enctype=“multipart/form-data”:將檔案統一轉成

二進位制

的形式上傳

method:以POST形式進行上傳

——>

<

form

action

=

“/upload”

enctype

=

“multipart/form-data”

method

=

“post”

>

<!—— name屬性是必須的,給後端用的 ——>

<!—— multiple屬性代表支援多選上傳 ——>

<

input

type

=

“file”

name

=

“fileInput”

multiple

>

<

br

>

<

input

type

=

“submit”

value

=

“上傳”

>

form

>

說清楚檔案上傳

上面HTML程式碼對應介面

此時選擇檔案後再點選上傳按鈕,上傳成功,如下圖所示:

說清楚檔案上傳

dir目錄

也會多出檔案,檔名字是後端可定義的,如下圖所示:

說清楚檔案上傳

以上我們沒有用到任何JS程式碼,就實現了最簡單的檔案上傳。後端/upload介面的實現也很簡單,先不用關心,後面我會給出原始碼展示並加以說明。

版本2:

版本1

存在的問題:上傳成功後當前頁面“不見了”,我們希望的是上傳成功後在當前頁面也能拿到後端返回的資訊,實際開發中也往往如此。我們對

版本1

的HTML程式碼進行改進,實際上只是增加了iframe標籤,並把form的target屬性值指向iframe的name值即可。

對應HTML程式碼

<!—— target 屬性規定在何處開啟 action URL ——>


我們選擇檔案並點選上傳按鈕後如下圖所示,可見後端返回的內容已經能在當前頁面接收了:

說清楚檔案上傳

版本3:

這個版本要做的工作是:

隱藏iframe框,我們頁面並不需要它

把iframe框中資料提取出來

簡單封裝下程式碼,假如頁面有N個上傳按鈕時方便使用

說清楚檔案上傳

我們的想法是,點選上圖中的上傳按鈕直接彈出系統檔案選擇框,如下所示:

說清楚檔案上傳

選中檔案後,點選開啟按鈕直接開始上傳操作。大致的操作思路如下圖:

說清楚檔案上傳

其中步驟3,我們用到了oFile。click()方法,IE8及以下瀏覽器是不支援的,也就是使用者必須手動點選框才能進行檔案選擇操作,解決辦法是:一開始在按鈕上覆蓋一個透明的框即可,不過我並不打算進行相關相容方法的處理。上面思路換成程式碼實現如下:

let

fileUpload

=

function

()

{

function

createIframe

opt

{

let

oIframe

=

document

createElement

‘iframe’

);

oIframe

name

=

opt

iframeName

oIframe

style

display

=

“none”

// none

document

body

appendChild

oIframe

);

return

oIframe

}

function

createForm

opt

{

let

oForm

=

document

createElement

“form”

);

oForm

action

=

opt

action

oForm

target

=

opt

iframeName

oForm

enctype

=

“multipart/form-data”

oForm

method

=

“post”

oForm

style

display

=

“none”

// none

// 建立表單內的File框

let

oFile

=

createFile

opt

);

oForm

appendChild

oFile

);

document

body

appendChild

oForm

);

return

{

oForm

oFile

};

}

function

createFile

opt

{

let

oFile

=

document

createElement

“input”

);

oFile

type

=

“file”

oFile

name

=

“fileInput”

oFile

multiple

=

true

oFile

accept

=

opt

type

join

‘,’

);

return

oFile

}

function

handleUpload

opt

{

// 建立iframe

let

oIframe

=

createIframe

opt

);

// 建立表單

let

obj

=

createForm

opt

);

// 插入完畢後執行點選

obj

oFile

click

();

// 監聽change,並執行submit提交

obj

oFile

addEventListener

“change”

()

=>

obj

oForm

submit

());

oIframe

addEventListener

“load”

opt

endFn

);

oIframe

addEventListener

“load”

function

()

{

// 我們可不希望每點選一次按鈕頁面都多出上面建立的標籤,所以拿到資料後把建立的標籤刪除

oIframe

remove

();

obj

oForm

remove

();

});

}

function

init

opt

{

opt

=

opt

||

{};

// 可以設定下opt的預設值,做下容錯處理

handleUpload

opt

);

}

return

{

init

init

}

})();

let

oBtn1

=

document

querySelector

“#btn1”

),

oBtn2

=

document

querySelector

“#btn2”

);

let

uploadOpt

=

{

iframeName

‘iframe’

// iframe name and form target

action

‘/upload’

// 上傳介面

type

‘image/jpeg’

‘image/png’

‘video/mp4’

],

// 接收上傳檔案型別

endFn

function

()

{

// 回撥函式,接收上傳成功後端返回的資料

let

oPre

=

this

contentDocument

querySelector

“pre”

);

let

data

=

JSON

parse

oPre

innerHTML

// 後端返回的資料data

console

log

data

);

}

};

// 點選上傳按鈕

oBtn1

addEventListener

“click”

()

=>

fileUpload

init

uploadOpt

));

oBtn2

addEventListener

“click”

()

=>

fileUpload

init

uploadOpt

));

版本4:

版本3

點選按鈕選擇檔案開啟後直接進行上傳操作,但對於上傳進度我們是不清楚的,在這個版本我們需要加上相關進度的展示。展示進度常用一下方式來實現:

Flash

基於WebSocket長連線方式去實現

輪訓(前端不斷向後端請求上傳進度)

用AJAX實現檔案上傳,AJAX提供有相關進度的介面(

版本5

中會用此方法進行改進)

以上方案各有優缺點,下面我用輪訓的方式實現下。簡單來說就是後端提供介面,前端每次請求實時返回檔案的上傳進度。就像下面的程式碼,

version_04。html

中也有完整的程式碼展示:

setInterval

lx

500

);

function

lx

()

{

let

xhr

=

new

XMLHttpRequest

xhr

open

‘post’

‘/progress’

);

xhr

send

();

xhr

onreadystatechange

=

function

()

{

if

xhr

readyState

==

4

&&

xhr

status

==

200

{

oProgress

value

=

parseInt

xhr

responseText

);

xhr

responseText

===

“100”

&&

clearInterval

timer

);

}

}

}

版本5:

其實上面4個版本我們都是在利用form表單,然後submit幫我們自動上傳,這個版本我們嘗試用FormData的形式獲取上傳資料(關於什麼是FormData,參考MDN的說明,使用非常簡單),並結合AJAX的形式把資料傳給後臺,這樣進度展示也可以利用AJAX提供的API在前端獲取。

說清楚檔案上傳

程式碼展示

function

UploadFile

()

{

this

oWrap

=

this

createEle

“div”

);

this

oBtn

=

this

createEle

“span”

);

}

// 建立標籤的函式

UploadFile

prototype

createEle

=

function

ele

{

return

document

createElement

ele

};

// 初始化頁面上的上傳按鈕

UploadFile

prototype

initBtn

=

function

opt

{

// btn

this

oBtn

className

=

“btn”

this

oBtn

innerHTML

=

“上傳檔案”

opt

cls

&&

this

oWrap

className

=

opt

cls

);

this

oWrap

appendChild

this

oBtn

);

document

body

appendChild

this

oWrap

);

};

// 建立FILE上傳框和進度條

UploadFile

prototype

initHtml

=

function

opt

{

this

oFile

=

this

createEle

“input”

);

this

oProgressWrap

=

this

createEle

“div”

);

this

oProgressCon

=

this

createEle

“span”

);

// file

this

oFile

type

=

“file”

this

oFile

style

display

=

“none”

opt

multiple

&&

this

oFile

multiple

=

opt

multiple

);

// progress

this

oProgressWrap

className

=

“progress-wrap”

this

oProgressCon

className

=

“progress-con”

this

oProgressWrap

appendChild

this

oProgressCon

);

opt

top

&&

this

oProgressCon

style

top

=

opt

top

+

“px”

);

this

oWrap

appendChild

this

oFile

);

this

oWrap

appendChild

this

oProgressWrap

);

};

// 用AJAX把資料傳給後臺

UploadFile

prototype

ajax

=

function

opt

{

let

xhr

=

new

XMLHttpRequest

_this

=

this

xhr

open

‘post’

‘/upload’

);

xhr

upload

addEventListener

“progress”

ev

=>

{

let

percent

=

ev

loaded

/

ev

total

*

100

+

“%”

this

oProgressCon

style

width

=

percent

});

xhr

send

opt

“formData”

]);

xhr

addEventListener

“readystatechange”

()

=>

{

if

xhr

readyState

===

4

&&

xhr

status

===

200

{

opt

cb

&&

opt

cb

JSON

parse

xhr

responseText

));

}

});

};

UploadFile

prototype

handle

=

function

opt

{

let

_this

=

this

this

oBtn

addEventListener

“click”

()

=>

{

// 下次點選的時候把上次建立的刪除

this

oFile

&&

this

oFile

remove

();

this

oProgressWrap

&&

this

oProgressWrap

remove

();

// 每次點選按鈕的時候把該建立的建立了,也可以在初始化的時候一次性把事辦齊

this

initHtml

opt

);

// 主動點選,彈出系統上傳框

this

oFile

click

();

// 監聽上傳框

this

oFile

addEventListener

“change”

()

=>

{

let

formData

=

new

FormData

();

// 透過FormData物件可以組裝一組用 XMLHttpRequest傳送請求的鍵/值對

formData

append

‘upload’

_this

oFile

files

0

]);

opt

“formData”

=

formData

this

ajax

opt

);

});

});

};

UploadFile

prototype

init

=

function

opt

{

opt

=

opt

||

{};

// 這裡可以搞一下預設引數的配置

this

initBtn

opt

);

this

handle

opt

);

};

/*

引數說明:

top: 進度條距離視窗頂部距離

cls: 外包裹class名字

multiple: 是否支援多選

cb: 上傳成功的回撥

accept: 支援上傳的檔案格式(未做)

還有一些細節要處理:

例如上一個還沒傳完,我又點選了相同的上傳按鈕這時候前端進度該如何處理

當碰到很大的檔案時,我們是否考慮切割上傳

前端是不是最好也把上傳預覽做一下

先到這裡,有空再來完善。。。

*/

let

btn1

=

new

UploadFile

();

let

btn2

=

new

UploadFile

();

btn1

init

({

top

0

cls

“wrap”

multiple

true

cb

function

data

{

console

log

data

);

}

});

btn2

init

({

top

“20”

});

檢視原始碼

Photo by Ian Stauffer

標簽: OPT  上傳  oFile  xhr  function