前端文件上传方式总结

1.使用FormData进行上传

这是比较主流的方式,也是兼容性最好的方式。

前端代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>转FormData传递数据</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);//三个参数分别对应key value和文件名
        }
        formData.append('fields',JSON.stringify(fields));//文件上传时携带的参数
        $.ajax({
            type: 'post',
            url: 'http://172.31.14.33:3000/formdata',
            data: formData,
            processData: false,//防止数据被转成字符串
            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');
    //预检请求的有效期,单位秒,在此期间不会再次发出预检请求
    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';//设置表单域编码
        form.maxFileSize = 10 * 1024 * 1024;//设置文件的大小限制,默认设置是200M
        form.maxFieldsSize = 2 * 1024 * 1024;//限制所有存储表单字段域的大小(除去file字段,并不是限制文件的大小),如果超出,则会触发error事件,默认为2M
        form.maxFields = 1000;//设置可以转换多少查询字符串,默认为1000
        form.uploadDir = __dirname + '/files';//设置文件零时存放的目录(需要建一个文件夹)
        form.keepExtensions = true;//是否保存文件原有的文件扩展名
        form.hash = false;//设置上传文件的检验码,可以有两个取值'sha1' or 'md5'.
        form.multiples = true;//开启该功能,当调用form.parse()方法时,
        form.on('progress', function (bytesReceived, bytesExpected) {
            console.log((bytesReceived / bytesExpected)*100 + '%');
        });
        form.on('error', err => {
            console.log(err);
        })
        // 多文件时回调函数的files参数的file属性是一个数组,数组每一个成员是一个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进行上传

由于某些老旧的浏览器对FileReaderAPI的兼容性问题,不建议在对兼容性要求高的项目里使用。

2.1使用FileReader上传小文件

直接将文件转成base64字符串发给后端,后端再将base64字符串中代表文件数据的字符串转成Buffer,然后再写成文件。

前端代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>转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字符串转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上传大文件

当文件较大时,base64字符串会长得难以想象,这会带来一些意料之外的问题。这时候可以将大文件转成ArrayBuffer进行传输,后端再将ArrayBuffer转成Buffer写入文件。

2.2.1不携额外参数时

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>转ArrayBuffer上传文件,不带其他参数</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',//也可以不设置
                },
                processData:false,//防止数据被转成字符串
                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');
    //预检请求的有效期,单位秒,在此期间不会再次发出预检请求
    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);//转成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需要携带额外参数时

可以在前端将ArrayBuffer转成Array,后端接收后再将Array转成Buffer即可。

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>转ArrayBuffer上传文件,带其他参数</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转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');
    //设置允许的请求方法
    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==='/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转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)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一丶前端文件上传方式 前端网页文件上传一般使用 来实现。 在 HTML 文档中: ` `标签每出现一次,一个...
    毒行影客阅读 7,154评论 0 13
  • 前端无法像原生APP一样直接操作本地文件,否则的话打开个网页就能把用户电脑上的文件偷光了,所以需要通过用户触发,用...
    雷波_viho阅读 832评论 0 1
  • 前端无法像原生APP一样直接操作本地文件,否则的话打开个网页就能把用户电脑上的文件偷光了,所以需要通过用户触发,用...
    孙悟空SUN阅读 426评论 0 0
  • 前言: 之前我总以为浏览器上传文件就一种方式——表单(表单包括 HTML 的 Form 表单,和虚拟表单 Form...
    CondorHero阅读 2,809评论 0 2
  • 我们在平时工作中常常会遇到文件上传的需求。但许久以来大多数人都是直接使用一些框架自带的组件去实现,对于一些复杂的上...
    SophieRabbit阅读 3,260评论 0 1