前端文件上傳方式總結(jié)

1.使用FormData進(jìn)行上傳

這是比較主流的方式,也是兼容性最好的方式钓简。

前端代碼
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>轉(zhuǎn)FormData傳遞數(shù)據(jù)</title>
</head>
<body>
<input type="file" id="file" multiple>
</body>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
    let $file = document.getElementById('file');
    $file.addEventListener('change', function (e) {
        let files = e.target.files;
        let formData=new FormData();
        let fields={
            id:'007',
            username:'張三',
        }
        for (const file of files) {
            formData.append('file',file,file.name);//三個參數(shù)分別對應(yīng)key value和文件名
        }
        formData.append('fields',JSON.stringify(fields));//文件上傳時攜帶的參數(shù)
        $.ajax({
            type: 'post',
            url: 'http://172.31.14.33:3000/formdata',
            data: formData,
            processData: false,//防止數(shù)據(jù)被轉(zhuǎn)成字符串
            contentType:false,
            success: function (res) {
                console.log(res);
            },
            error: function (err) {
                console.log(err);
            }
        })
    })
</script>
</html>
后端代碼
const http = require('http')
const port = 3000
const fs = require('fs')
const formidable = require('formidable')
http.createServer((req, res) => {
    //跨域處理
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
    res.setHeader('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
    //預(yù)檢請求的有效期,單位秒,在此期間不會再次發(fā)出預(yù)檢請求
    res.setHeader('Access-Control-Max-Age', 36000)
    if (req.method === 'OPTIONS') {
        res.writeHead(200);
        res.end();
    }
    let path = req.url.split('?')[0];
    if (path === '/formdata') {
        let form = formidable.IncomingForm();
        form.encoding = 'utf-8';//設(shè)置表單域編碼
        form.maxFileSize = 10 * 1024 * 1024;//設(shè)置文件的大小限制,默認(rèn)設(shè)置是200M
        form.maxFieldsSize = 2 * 1024 * 1024;//限制所有存儲表單字段域的大谐叟(除去file字段,并不是限制文件的大泄睢)陨簇,如果超出,則會觸發(fā)error事件彼哼,默認(rèn)為2M
        form.maxFields = 1000;//設(shè)置可以轉(zhuǎn)換多少查詢字符串对妄,默認(rèn)為1000
        form.uploadDir = __dirname + '/files';//設(shè)置文件零時存放的目錄(需要建一個文件夾)
        form.keepExtensions = true;//是否保存文件原有的文件擴(kuò)展名
        form.hash = false;//設(shè)置上傳文件的檢驗碼,可以有兩個取值'sha1' or 'md5'.
        form.multiples = true;//開啟該功能沪羔,當(dāng)調(diào)用form.parse()方法時饥伊,
        form.on('progress', function (bytesReceived, bytesExpected) {
            console.log((bytesReceived / bytesExpected)*100 + '%');
        });
        form.on('error', err => {
            console.log(err);
        })
        // 多文件時回調(diào)函數(shù)的files參數(shù)的file屬性是一個數(shù)組,數(shù)組每一個成員是一個File對象蔫饰,此功能需要 html5中multiple特性支持琅豆。
        form.parse(req, function (err, fields, files) {
            let info = JSON.parse(fields.fields);
            console.log(info);//{ id: '007', username: '張三' }
            console.log(files);
            if (files.file.length) {
                //多文件
                let count = 0;
                for (const file of files.file) {
                    //更改文件名
                    fs.rename(file.path, './files/' + file.name + '', err => {
                        if (err) {
                            console.log(err);
                            res.writeHead(500);
                            res.end(JSON.stringify({files, fields}));
                        }
                        count++;
                        if (count === files.file.length) {
                            res.writeHead(200);
                            res.end(JSON.stringify({files, fields}));
                        }
                    })
                }
            } else {
                //單文件
                let file = files.file;
                //更改文件名
                fs.rename(file.path, './files/' + file.name + '', err => {
                    if (err) {
                        console.log(err);
                        res.writeHead(500);
                        res.end(JSON.stringify({files, fields}));
                    }
                    res.writeHead(200);
                    res.end(JSON.stringify({files, fields}));
                })
            }
        })
    }
}).listen(port)

2.使用FileReader進(jìn)行上傳

由于某些老舊的瀏覽器對FileReaderAPI的兼容性問題,不建議在對兼容性要求高的項目里使用篓吁。

2.1使用FileReader上傳小文件

直接將文件轉(zhuǎn)成base64字符串發(fā)給后端茫因,后端再將base64字符串中代表文件數(shù)據(jù)的字符串轉(zhuǎn)成Buffer,然后再寫成文件杖剪。

前端代碼
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>轉(zhuǎn)base64上傳文件</title>
</head>
<body>
<input type="file" id="file">
</body>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
    let $file=document.getElementById('file');
    $file.addEventListener('change',function (e) {
        let file=e.target.files[0];
        let filename=file.name;
        console.log(file);
        let fileReader=new FileReader();
        fileReader.onload=function(e){
            let base64=e.target.result;//base64字符串
            $.ajax({
                type:'post',
                url:'http://172.31.14.33:3000/base64File',
                data:JSON.stringify({
                    filename:filename,
                    base64:base64,
                }),
                success:function (res) {
                    console.log(res);
                },
                error:function (err) {
                    console.log(err);
                }
            })
        }
        fileReader.readAsDataURL(file);
    })
</script>
</html>
后端代碼
const http = require('http')
const port = 3000
const fs = require('fs')
http.createServer((req, res) => {
    //跨域處理
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
    res.setHeader('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
    res.setHeader('Access-Control-Max-Age', 36000)
    if (req.method === 'OPTIONS') {
        res.writeHead(200);
        res.end();
    }
    let path=req.url.split('?')[0];
    if(path==='/base64File'&&req.method==='POST'){
        let str='';
        req.on('data',function (chunk) {
            str+=chunk;
        })
        req.on('end',function () {
            let data=JSON.parse(str);
            let buffer=Buffer.from(data.base64.split(',')[1],'base64');//base64字符串轉(zhuǎn)Buffer
            fs.writeFile('./'+data.filename,buffer,function (err) {
                if(err){
                    console.log(err);
                    res.writeHead(500);
                    res.end('error');
                }else {
                    res.writeHead(200);
                    res.end('success');
                }
            })
        })
    }
}).listen(port)

2.2使用FileReader上傳大文件

當(dāng)文件較大時冻押,base64字符串會長得難以想象,這會帶來一些意料之外的問題盛嘿。這時候可以將大文件轉(zhuǎn)成ArrayBuffer進(jìn)行傳輸洛巢,后端再將ArrayBuffer轉(zhuǎn)成Buffer寫入文件。

2.2.1不攜額外參數(shù)時

前端代碼

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>轉(zhuǎn)ArrayBuffer上傳文件次兆,不帶其他參數(shù)</title>
</head>
<body>
<input type="file" id="file">
</body>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
    let $file = document.getElementById('file');
    $file.addEventListener('change', function (e) {
        let file = e.target.files[0];
        let fileReader = new FileReader();
        fileReader.onload = function (e) {
            let arraybuffer = e.target.result;//ArrayBuffer
            $.ajax({
                type: 'post',
                url: 'http://172.31.14.33:3000/arraybuffer_no_options',
                data: arraybuffer,
                headers:{
                    'Content-Type':'application/octet-stream',//也可以不設(shè)置
                },
                processData:false,//防止數(shù)據(jù)被轉(zhuǎn)成字符串
                success: function (res) {
                    console.log(res);
                },
                error: function (err) {
                    console.log(err);
                }
            })
        }
        fileReader.readAsArrayBuffer(file);
    })
</script>
</html>

后端代碼

const http = require('http')
const port = 3000
const fs = require('fs')
http.createServer((req, res) => {
    //跨域處理
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
    res.setHeader('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
    //預(yù)檢請求的有效期,單位秒,在此期間不會再次發(fā)出預(yù)檢請求
    res.setHeader('Access-Control-Max-Age', 36000)
    if (req.method === 'OPTIONS') {
        res.writeHead(200);
        res.end();
    }
    let path = req.url.split('?')[0];
    if (path === '/arraybuffer_no_options' && req.method === 'POST') {
        let arr = [];
        req.on('data', function (chunk) {
            arr.push(chunk);
        })
        req.on('end', function () {
            let buffer = Buffer.concat(arr);//轉(zhuǎn)成Buffer
            console.log(buffer);
            fs.writeFile('./test.png', buffer, function (err) {
                if (err) {
                    console.log(err);
                    res.writeHead(500);
                    res.end('error');
                } else {
                    res.writeHead(200);
                    res.end('success');
                }
            })
        })
    }
}).listen(port)
2.2.2需要攜帶額外參數(shù)時

可以在前端將ArrayBuffer轉(zhuǎn)成Array稿茉,后端接收后再將Array轉(zhuǎn)成Buffer即可。

前端代碼

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>轉(zhuǎn)ArrayBuffer上傳文件芥炭,帶其他參數(shù)</title>
</head>
<body>
<input type="file" id="file">
</body>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
    let $file = document.getElementById('file');
    $file.addEventListener('change', function (e) {
        let file = e.target.files[0];
        let fileReader = new FileReader();
        fileReader.onload = function (e) {
            let arraybuffer = e.target.result;//ArrayBuffer
            //ArrayBuffer轉(zhuǎn)Array
            let array = Array.prototype.slice.call(new Uint8Array(arraybuffer));
            $.ajax({
                type: 'post',
                url: 'http://172.31.14.33:3000/arraybuffer_has_options',
                data: JSON.stringify({
                    filename:file.name,
                    u8arr:array,
                }),
                success: function (res) {
                    console.log(res);
                },
                error: function (err) {
                    console.log(err);
                }
            })
        }
        fileReader.readAsArrayBuffer(file);
    })
</script>
</html>

后端代碼

const http = require('http')
const port = 3000
const fs = require('fs')
http.createServer((req, res) => {
    //跨域處理
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
    //設(shè)置允許的請求方法
    res.setHeader('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
    //預(yù)檢請求的有效期,單位秒,在此期間不會再次發(fā)出預(yù)檢請求
    res.setHeader('Access-Control-Max-Age', 36000);
    if (req.method === 'OPTIONS') {
        res.writeHead(200);
        res.end();
    }
    let path=req.url.split('?')[0];
    if(path==='/arraybuffer_has_options'&&req.method==='POST'){
        let msg='';
        req.on('data',function (chunk) {
            msg+=chunk;
        })
        req.on('end',function () {
            let data=JSON.parse(msg);
            let buffer = new Buffer.from(data.u8arr);//Array轉(zhuǎn)Buffer
            fs.writeFile('./'+data.filename,buffer,function (err) {
                if(err){
                    console.log(err);
                    res.writeHead(500);
                    res.end('error');
                }else {
                    res.writeHead(200);
                    res.end('success');
                }
            })
        })
    }
}).listen(port)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末漓库,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子园蝠,更是在濱河造成了極大的恐慌渺蒿,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彪薛,死亡現(xiàn)場離奇詭異茂装,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)陪汽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門训唱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人挚冤,你說我怎么就攤上這事况增。” “怎么了训挡?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵澳骤,是天一觀的道長。 經(jīng)常有香客問我澜薄,道長为肮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任肤京,我火速辦了婚禮颊艳,結(jié)果婚禮上茅特,老公的妹妹穿的比我還像新娘。我一直安慰自己棋枕,他們只是感情好白修,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著重斑,像睡著了一般兵睛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上窥浪,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天祖很,我揣著相機(jī)與錄音,去河邊找鬼漾脂。 笑死假颇,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的骨稿。 我是一名探鬼主播拆融,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼啊终!你這毒婦竟也來了镜豹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤蓝牲,失蹤者是張志新(化名)和其女友劉穎趟脂,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體例衍,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昔期,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了佛玄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片硼一。...
    茶點(diǎn)故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖梦抢,靈堂內(nèi)的尸體忽然破棺而出般贼,到底是詐尸還是另有隱情,我是刑警寧澤奥吩,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布哼蛆,位于F島的核電站,受9級特大地震影響霞赫,放射性物質(zhì)發(fā)生泄漏腮介。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一端衰、第九天 我趴在偏房一處隱蔽的房頂上張望叠洗。 院中可真熱鬧甘改,春花似錦、人聲如沸灭抑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽名挥。三九已至,卻和暖如春主守,著一層夾襖步出監(jiān)牢的瞬間禀倔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工参淫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留救湖,地道東北人。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓涎才,卻偏偏與公主長得像鞋既,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子耍铜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容

  • 一丶前端文件上傳方式 前端網(wǎng)頁文件上傳一般使用 來實現(xiàn)邑闺。 在 HTML 文檔中: ` `標(biāo)簽每出現(xiàn)一次,一個...
    毒行影客閱讀 7,122評論 0 13
  • 前端無法像原生APP一樣直接操作本地文件棕兼,否則的話打開個網(wǎng)頁就能把用戶電腦上的文件偷光了陡舅,所以需要通過用戶觸發(fā),用...
    雷波_viho閱讀 821評論 0 1
  • 前端無法像原生APP一樣直接操作本地文件伴挚,否則的話打開個網(wǎng)頁就能把用戶電腦上的文件偷光了靶衍,所以需要通過用戶觸發(fā),用...
    孫悟空SUN閱讀 389評論 0 0
  • 前言: 之前我總以為瀏覽器上傳文件就一種方式——表單(表單包括 HTML 的 Form 表單茎芋,和虛擬表單 Form...
    CondorHero閱讀 2,691評論 0 2
  • 我們在平時工作中常常會遇到文件上傳的需求颅眶。但許久以來大多數(shù)人都是直接使用一些框架自帶的組件去實現(xiàn),對于一些復(fù)雜的上...
    SophieRabbit閱讀 3,186評論 0 1