1. 使用XMLHttpRequest

后台代码:

const express = require('express')
const app = express()
app.use(express.static("public")) // 静态资源目录
// FOR:"Content-type":"application/x-www-form-urlencoded"
app.use(express.urlencoded({extended: false }));
// FOR:"Content-type":"application/json"
// app.use(express.json()) 
app.post('/calc', (req, res) => {
	console.log(req.body) // MUST ADD app.use(...)
	setTimeout(()=>res.send(`<div style="background-color:tomato;
		border-radius:50%;width:${req.body.w}px;height:${req.body.h}px"/>`), 1000)
})
app.listen(8080)

public/index.html里面可以搞一个按钮来发送ajax请求(可以使用JQUERY):

<!DOCTYPE html>
<html>
<head>	<meta charset="utf-8"><title></title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
	<form action="post">
		<div>宽:<input type="text" id="w" placeholder="input width"></div>
		<div>高:<input type="text" id="h" placeholder="input height"></div>
		<div><input type="button" id="calc" onclick="Calc()" value="calc" /></div>
	</form>
    <div id="result"></div>
</body>
<script>
	function Calc() {
		let xhr = new XMLHttpRequest() // 创建XHR
		let w = parseFloat(document.getElementById('w').value)
		let h = parseFloat(document.getElementById('h').value)		
		xhr.open('post', '/calc', true)// 方法,路径,异步
		xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
		//xhr.responseType = "json";// 响应BODY的 content-type类型:json/text...
		xhr.onload = function (e) {
			if(this.status==200)//HTTP status code
				// 注意:不是oReq.responseText
				document.getElementById("result").innerHTML = this.response 
        }
		xhr.send(`w=${w}&h=${h}`);
        //xhr.setRequestHeader("Content-type", "application/json");
		//xhr.send(JSON.stringify({w, h})); // use JSON format
		document.getElementById("result").innerHTML = `WAITING...` //  waiting...
		console.log("OK")
	}
    // 方法二
    /*
    $("#calc").on('click', () => {
        let w = $("#w").val()
        let h = $("#h").val()
        $.ajax({
            type: 'post', 
            url: '/calc',  // xhr.open('post','/calc',true)
            contentType: 'application/json', // xhr.setRequestType('application/json')
            data: JSON.stringify({w,h}), // xhr.send(JSON.stringify({name}))
            success: function (response) { // xhr.onload =
                console.log(response)
                $("#result").innerHTML = (response)
            }, 
            error: function (e) { } // xhr.onerror
        })
    })
    */
</script>
</html>

2 使用表单FormData & Fetch API

重点在于FormDataformidable的配合使用,注意不要使用form的action/onsubmit,会导致AJAX返回数据看不到。

npm install formidable -S

<form id="form1" style="width:245px">
    用户名:<input type='text' name="name" placeholder="input your name" />
    <br><br>
    密码:<input type='password' name="pswd" placeholder="input your password"/>
    <br><br>
    <input type="button" onclick="Send()" value="send" />
</form>
<script>
	async function Send() {		
		var form = document.forms.namedItem("form1");
		//将htmL表单转换为FormData表单对象
		var formData = new FormData(form);
		console.log(formData.get("image_uploads"))
		let res = await fetch('/login', {
            method: 'post',body:formData // json需要转字符串;这里可以直接传递formdata
        }) 
		let json = await res.json()  // text() blob()
        console.log(json)
	}
</script>

后台使用formidable自动解析req:

const express = require('express')
const formiable = require('formidable')
const app = express()
app.listen(8080)
app.use(express.static('public'))
app.post('/login', (req, res) => {
	let form1 = formiable({
		uploadDir: path.join(__dirname, "uploads"),    //设置上传目录
		multiples: true, // 多文件
		allowEmptyFiles: false, // 不允许空文件
		keepExtensions: true  //保持原有扩展名
	});
	form1.parse(req, (err, fields, files) => {
		console.log(fields.name,fields.pswd)// 2.fileds即form1数据{name,pswd}
        //...
	})
})

3.上传文件

上传文件和普通的表单并无不同:

<form id="form1" >
    上传头像<input type="file" required multiple
		id="file1" accept=".jpg, .jpeg, .png" name="image_uploads">
		<input type="button" onclick="Send()" value="send" />
    <img id="res"/>
</form>
<script>
  function Send() {		
		var form = document.forms.namedItem("form1");
		//将htmL表单转换为FormData表单对象
		var formData = new FormData(form);
		console.log(formData.get("image_uploads"))
		let xhr = new XMLHttpRequest()
		xhr.open('post', '/upload', true);
		xhr.responseType = 'json'	
		xhr.onload = function () {//3
			if (this.status == 200) {
				console.log(xhr.response)
				document.getElementById('res').src = this.response
			}
		}
		xhr.send(formData)// 1
	}
</script>

后台处理一下files字段即可:

app.post('/upload', async (req, res) => {
	let form1 = formiable({
		uploadDir: path.join(__dirname, "uploads"),    //设置上传目录
		multiples: true, // 多文件
		allowEmptyFiles: false, // 不允许空文件
		keepExtensions: true  //保持原有扩展名
	});
	form1.parse(req, async (err, fields, files) => {		
		const multiples = Array.isArray(files.image_uploads); // 多文件
		if (multiples) { //多个文件
			const ret = []
			for (let file of files.image_uploads) {
				let oldFilename = file.filepath //本地路径
				const imgBase64 = await readFileBase64(oldFilename)
				ret.push(imgBase64) //【base64 image】
			}
			return res.send(JSON.stringify(ret))
		} else if (files.image_uploads) { // 单文件
			console.log("SDFSD",files.image_uploads)
			let oldFilename = files.image_uploads.filepath
			// 方法1,使用URL,发送一个 path url!
			// res.send(files.attrName.filepath.split('public')[1])
			const imgBase64 = await readFileBase64(oldFilename)
			return res.send({path:imgBase64})
		}
		else res.send("[]")
	})
})
// 图片文件转为base64图片
async function readFileBase64(file) {
	try {
		const res = await fs.readFile(file);
		const mtype = mime.getType(file)
		var base64str = res.toString('base64');
		// START WITH `data:image/png;base64,`
		return `data:${mtype};base64,${base64str}`
	} catch (err) {
		console.error(err);
	}
	return null;
}

4. 跨源资源共享 CORS

出于安全性,浏览器限制脚本内发起的跨源HTTP请求(script/img/iframe标签除外)

解决方法分为前端和后端。这里使用前端jsonp方案(后端使用request包,类似爬虫方案)。首先前台代码使用<script>跨域访问服务器localhost:3001

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@localhost:3000</title>
</head>
<body>
    <script>
        function fn(data){console.log(`fn return: ${data}`)}
    </script>
    <script src="http://10.201.17.100:3001/jsonp?a=1&b=2"></script>
</body>
</html>

后台3000端口模拟本地服务器A,3001模拟服务器B,代码如下:

 //服务器A
const express = require('express')
const app = express()

app.listen(3000)
app.use(express.static('public'))
 //服务器B
const express = require('express')
const app = express()
app.listen(3001)
app.use(express.urlencoded({extended:false}))
app.get('/jsonp', (req, res)=>{
    console.log(req.query)
    let c = parseInt(req.query.a) +  parseInt(req.query.b) // 计算 a+b
    res.send(`fn(${c})`)
})