前言前言前言文件上传如果加上进度条会有更好的用户体验(尤其是中大型文件),本文使用 Nodejs 配合前端完成这个功能。Nodejs 前端我们使用 FormData 来作为载体发送数据。FormData 效果
前端部分前端部分前端部分HTML 部分 和 Js 部分



// 获取 input file 的 dom 对象
const inputFile = document.querySelector('#file');

// 监听 change 事件
inputFile.addEventListener('change', function() {
// 使用 formData 装载 file
const formData = new FormData();
formData.append('file', this.files[0]);

// 上传文件
upload(formData);
})



// 获取 input file 的 dom 对象
const inputFile = document.querySelector('#file');

// 监听 change 事件
inputFile.addEventListener('change', function() {
// 使用 formData 装载 file
const formData = new FormData();
formData.append('file', this.files[0]);

// 上传文件
upload(formData);
})下面我们实现 upload 方法。使用 XMLHttpRequest 的方式
const upload = ( formData ) => {
const xhr = new XMLHttpRequest();
// 监听文件上传进度
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
// 获取进度
const progress = Math.round((e.loaded * 100) / e.total);

document.querySelector('#progress').setAttribute('value', progress);
}
},false);

// 监听上传完成事件
xhr.addEventListener('load', ()=>{
console.log(':smile:上传完成')
}, false);

xhr.open('post', 'http://127.0.0.1:3000/upload');
xhr.send(formData);
}
const upload = ( formData ) => {
const xhr = new XMLHttpRequest();
// 监听文件上传进度
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
// 获取进度
const progress = Math.round((e.loaded * 100) / e.total);

document.querySelector('#progress').setAttribute('value', progress);
}
},false);

// 监听上传完成事件
xhr.addEventListener('load', ()=>{
console.log(':smile:上传完成')
}, false);

xhr.open('post', 'http://127.0.0.1:3000/upload');
xhr.send(formData);
}使用 jQuery 的 ajax 上传使用 jQuery 的 ajax 上传使用 jQuery 的 ajax 上传jQuery 目前的使用量依然庞大,那么使用 jQuery 的 ajax 如何监听文件上传进度呢:
const upload = ( formData ) => {
$.ajax({
type: 'post',
url: 'http://127.0.0.1:3000/upload',
data: formData,
// 不进行数据处理和内容处理
processData: false,
contentType: false,
// 监听 xhr
xhr: function() {

const xhr = $.ajaxSettings.xhr();

if (xhr.upload) {

xhr.upload.addEventListener('progress', e => {

const { loaded, total } = e;

var progress = (loaded / total) * 100;

document.querySelector('#progress').setAttribute('value', progress);

},

false

);

return xhr;

}
},
success: function(response) {

console.log('上传成功');
}
});
}
const upload = ( formData ) => {
$.ajax({
type: 'post',
url: 'http://127.0.0.1:3000/upload',
data: formData,
// 不进行数据处理和内容处理
processData: false,
contentType: false,
// 监听 xhr
xhr: function() {

const xhr = $.ajaxSettings.xhr();

if (xhr.upload) {

xhr.upload.addEventListener('progress', e => {

const { loaded, total } = e;

var progress = (loaded / total) * 100;

document.querySelector('#progress').setAttribute('value', progress);

},

false

);

return xhr;

}
},
success: function(response) {

console.log('上传成功');
}
});
}使用 axios 上传并监听进度使用 axios 上传并监听进度使用 axios 上传并监听进度axios 使用量非常大,用它监听文件上传更简单,代码如下:
const upload = async ( formData ) => {

let config = {
// 注意要把 contentType 设置为 multipart/form-data
headers: {

'Content-Type': 'multipart/form-data'
},

// 监听 onUploadProgress 事件
onUploadProgress: e => {

const {loaded, total} = e;

// 使用本地 progress 事件

if (e.lengthComputable) {

let progress = loaded / total * 100;

document.querySelector('#progress').setAttribute('value', progress);

}
}
};

const { status } = await axios.post('http://127.0.0.1:3000/upload', formData, config);
if (res.status === 200) {

console.log('上传完成:grinning:');
}
}
const upload = async ( formData ) => {

let config = {
// 注意要把 contentType 设置为 multipart/form-data
headers: {

'Content-Type': 'multipart/form-data'
},

// 监听 onUploadProgress 事件
onUploadProgress: e => {

const {loaded, total} = e;

// 使用本地 progress 事件

if (e.lengthComputable) {

let progress = loaded / total * 100;

document.querySelector('#progress').setAttribute('value', progress);

}
}
};

const { status } = await axios.post('http://127.0.0.1:3000/upload', formData, config);
if (res.status === 200) {

console.log('上传完成:grinning:');
}
}Nodejs 部分
Nodejs 部分
Nodejs 部分
这部分比较简单,其实就是单纯的文件上传,我们用 Koa 来实现.环境搭建及依赖包安装环境搭建及依赖包安装环境搭建及依赖包安装这里使用 koa2 ,安装以下依赖包:

koa

@koa/router: koa 的路由

@koa/cors:用于跨域

koa-body: 解析 body 数据

nodemon: 使用它启动服务,带有热更新


koa@koa/router: koa 的路由@koa/cors:用于跨域koa-body: 解析 body 数据nodemon: 使用它启动服务,带有热更新

代码部分
const Koa = require('koa');
const Router = require('@koa/router');
const koaBody = require('koa-body');
const path = require('path');
const fs = require('fs');
const cors = require('@koa/cors');

const app = new Koa();
const router = new Router();

router.all('/upload', async ctx => {
// 处理文件上传
const res = await dealFile(ctx);

res && (ctx.body = {
status: 200,
msg: 'complete'
});
});

// 中间件部分
app.use(cors());
app.use(
koaBody({
multipart: true,
formidable: {
maxFileSize: 2000 * 1024 * 1024 //最大2G
}
})
);
app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000);
const Koa = require('koa');
const Router = require('@koa/router');
const koaBody = require('koa-body');
const path = require('path');
const fs = require('fs');
const cors = require('@koa/cors');

const app = new Koa();
const router = new Router();

router.all('/upload', async ctx => {
// 处理文件上传
const res = await dealFile(ctx);

res && (ctx.body = {
status: 200,
msg: 'complete'
});
});

// 中间件部分
app.use(cors());
app.use(
koaBody({
multipart: true,
formidable: {
maxFileSize: 2000 * 1024 * 1024 //最大2G
}
})
);
app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000);dealFile 方法处理上传的文件dealFile 方法处理上传的文件dealFile 方法处理上传的文件出于性能考虑,操作 file 毫无疑问要使用 stream 。我们要监听文件流 end 事件,由于无法在事件回调里返回响应,因为会报 404,所以需要使用 Promise 来封装一下,然后用 async、awaitPromise
const dealFile = ctx => {
const { file } = ctx.request.files;

const reader = fs.createReadStream(file.path);
const writer = fs.createWriteStream(
// 文件上传到 image 文件夹中
path.resolve(__dirname, './image', file.name)
);

return new Promise((resove, reject) => {

reader.pipe(writer);

reader.on('end', () => {
resove(true);
});

reader.on('error', err => {
throw err;
})

});
};
const dealFile = ctx => {
const { file } = ctx.request.files;

const reader = fs.createReadStream(file.path);
const writer = fs.createWriteStream(
// 文件上传到 image 文件夹中
path.resolve(__dirname, './image', file.name)
);

return new Promise((resove, reject) => {

reader.pipe(writer);

reader.on('end', () => {
resove(true);
});

reader.on('error', err => {
throw err;
})

});
};到这里就全部完成了。这里注意一下:前端监听文件进度不需要后端有什么特殊处理,后端仅仅是做了文件流的写入而已。总结总结总结