AJAX
AJAX(Asynchronous Javascript And XML)指的是在web应用程序中异步向服务器发送请求。它的实现方式有两种XMLHttpRequest(简称XHR)和Fetch。
XHR和Fetch的比较
练习例子(图片上传)
请求客户端
前端主要功能实现
- 图片预览
给input标签添加onchange事件
const selectFile=document.querySelector('.upload-select input');
selectFile.onchange = (e) => {
//获取选择的文件
const file = e.target.files[0];
// 显示预览图
//文件读取对象
const reader = new FileReader();
//读取文件 并将文件读取为DataURL形式
reader.readAsDataURL(file); // 将文件数据读取为 DataURL(base64编码)
//读取完成后
reader.onload = (e) => {
// 预览图片
doms.img.src = e.target.result;
//将图片上传至服务器中
upload(file);
};
};
- 图片上传进度监控与上传
function upload(file) {
setProgress(0);
// XHR
const xhr = new XMLHttpRequest();
//配置了一个POST请求 url为http://localhost:9527/upload/single
xhr.open('POST', 'http://localhost:9527/upload/single'); // 配置请求
//实时监听文件上传的进度,并执行相关代码
xhr.upload.onprogress = (e) => {
//计算上传的
const percent = Math.floor((e.loaded / e.total) * 100);
//前端显示上传监控 //图片太小 一下就上传完成了
setProgress(percent);
};
//上传完成后,执行相关代码
xhr.onload = () => {
showArea('result');
};
//上传图片/文件时通常使用 multipart/form-data格式请求体
const form = new FormData(); // 生成multipart/form-data格式的请求体
form.append('avatar', file);
//发送请求 form为请求体(POST请求的请求体)
xhr.send(form);
}
服务端主要功能实现
- 服务器的搭建
//导入express框架 用于构建Nodejs服务器
const express = require('express');
//导入body-parser中间件 用于解析HTTP的请求体
const bodyParser = require('body-parser');
//导入cors中间件 用于处理跨域请求
const cors = require('cors');
//导入配置文件
const config = require('./config');
const app = express();
// 允许跨域请求
app.use(cors());
//解析json格式的请求体 use表示启动这个bodyParser.json 中间件 并且请求体的大小限制为50MB
app.use(bodyParser.json({ limit: '50mb' }));
//挂载路由 当请求url为当前路由时,就会进入./upload/single 中,从而处理请求
app.use('/upload/single', require('./upload/single'));
//错误处理
app.use((err, req, res, next) => {
const { UploadError } = require('./upload/errorTypes');
if (err instanceof UploadError) {
res.send({
errCode: err.code,
errMsg: err.message,
});
}
console.log(err);
});
app.listen(config.global.port, () => {
console.log(`server start on port ${config.global.port}`);
});
- ./upload/single路由处理
//引入相关配置
const config = require('../config');
//引入multer模块 (处理multipart/form-data类型的表单数据)
const multer = require('multer');
//创建一个multer实例
/*
dest:文件存储地址
limits:文件大小限制
fileFilter:文件过滤函数(检查是否为符合要求的文件后缀名)
*/
const upload = multer({
dest: config.global.uploadPath,
limits: {
fileSize: config.single.sizeLimit,
},
//cb callback 表示回调函数
fileFilter(req, file, cb) {
const path = require('path');
const ext = path.extname(file.originalname);
if (config.single.exts.includes(ext)) {
//文件经过过滤后,需要确定是否接收该文件
//使用 cb(null,true) 来表示接收
cb(null, true);
} else {
//使用 cb(null,false) 来表示拒绝接收
const { ExtError } = require('./errorTypes');
//发出一个错误
cb(new ExtError());
}
},
});
//创建一个路由实例
const express = require('express');
const router = express.Router();
//这个路由实例会处理url('./upload/single/')下的post请求
//接收到请求后,执行相应的回调函数
// req http请求对象 res http响应对象 next 如果当前的回调函数不处理请求时,可以使用next函数将请求对象传递给下一个处理函数
router.post('/', async (req, res, next) => {
//使用multer模块中的单文件处理函数 处理接收到的文件
upload.single(config.single.fieldName)(req, res, async (err) => {
//如果发生错误 判断错类型
if (err instanceof multer.MulterError) {
const { SizeLimitError, UnexpectedRequest } = require('./errorTypes');
if (err.message === 'File too large') {
err = new SizeLimitError();
} else {
err = new UnexpectedRequest();
}
}
if (err) {
next(err);
return;
}
//返回响应数据
res.send({
data: '成功上传',
});
});
});
知识点补充
文件对象
在nodejs服务器中,通过req.file获取到从前端传过来的文件对象。(多文件对象 使用req.files)
req.file对象有以下属性
| 属性名 | 含义 |
|---|---|
fieldname | 文件数据对应的name(multipart/form-data格式中的name属性) |
originalname | 上传时 文件的名称 |
encoding | 上传的文件编码 |
mimetype | 上传的文件的MIME类型 |
size | 上传的文件的大小 字节数 |
destination | 文件保存的路径(在服务器中) 不包含文件名的路径 |
filename | 保存的文件名(没有后缀) |
path | 保存的完整路径 包含文件名 |
multer模块
multer是一个nodejs中间件,用于处理multipart/form-data类型的表单数据,主要用于上传文件。
multer不会处理非multipart/form-data类型的表单数据
创建multer实例时,有一个options对象。下面是options对象属性具体含义。
| 属性名 | 含义 |
|---|---|
dest | 上传文件的存储位置 |
storage | 存储引擎对象它允许你自定义文件的存储逻辑,包括指定存储目录、文件名生成规则、文件过滤等 |
fileFilter | 文件过滤函数 |
limits | 限制上传文件的大小 |
preservePath | 保存包含文件名的完整文件路径 |
storage
1、磁盘存储引擎对象 diskStorage
const storage=multer.diskStorage({
destination:function(req,file,cb){
cb(null,'/upload')
},
filename:function(req,file,cb){
cb(null,file.fileldname+Date.now())
}
})
const upload=multer({storage:storage})
destination 表示一个设置文件存储目录的函数 这里通过设置条件,从而实现不同类型文件存储进不同目录 (也可以直接设置一个目录地址string '/upload')
filename 表示一个设置文件名称的函数
2、内存存储引擎对象 memoryStorage
memoryStorage将上传的文件存储在内存中,而不是写入磁盘。这种存储方式适用于临时存储小文件,上传后立即处理,不需要持久化存储的场景。
const storage=multer.memoryStorage()
const uypload=multer({storage:storage})
single
单文件上传
//配置multer
const upload = multer({
dest: config.global.uploadPath,
limits: {
fileSize: config.single.sizeLimit,
},
fileFilter(req, file, cb) {
const path = require('path');
//获取文件的扩展名
const ext = path.extname(file.originalname);
// 检查文件扩展名是否在允许的扩展名列表中
if (config.single.exts.includes(ext)) {
//存在 接收文件
cb(null, true);
} else {
//不存在 拒绝文件
// cb(null, false);
const { ExtError } = require('./errorTypes');
//发出一个错误 并终止请求 并返回错误信息
cb(new ExtError());
}
},
});
//设置相应路由post请求的回调函数
router.post('/', (req,res,next)=>{
//单文件处理(multer会自动给上传的文件附上随机名称)
upload.single(config.single.fieldName)(req,res,(err)=>{
//判断错误类型
if(err instanceof multer.MulterError){
const {SizeLimitError,UnexpectedRequest} =require('./errorTypes');
if(err.message==='File too large'){
err=new SizeLimitError();
}else{
err=new UnexpectedRequest();
}
}
if(err){
//发生错误,传给下一个处理
next(err);
return;
}
})
})
array
多文件上传
const upload=muter({
dest:config.global.uploadPath,
limits:{
fileSize:config.multer.sizeLimit,
},
fileFilter(req,file,cb){
const path=require('path');
const ext=path.extname(file.originalname);
if(config.multi.exts.includes(ext))
{
cb(null,true);
}else{
const {ExtError }=require('./errorTypes');
cb(new ExtError());
}
}
})
router.post('/',(req,res,next)=>{
upload.array(config.multi.fieldName,config.multi.countLimit)(req,res,(err)=>{
if(err instanceof multer.MulterError){
const {SizeLimitError,CountLimitError} =require('./errorTypes');
if(err.message==='File too large'){
err=new SizeLimitError();
}else{
//文件数量错误
err=new CountLmitError();
}
}
if(err){
next(err);
return;
}
})
})