Day13
今日份内容:使用XMLHttpRequest 2制作基于Node.js的用户修改头像
在HTML5规范中对XMLHttpRequest对象的send()方法进行改良后,其可以发送字符串、document对象、表单数据、Blob对象、文件以及ArrayBuffer对象。
XMLHttpRequest 2
- 了解XMLHttpRequest与服务器异步交互
- 使用XMLHttpRequest向服务器提交数据
内容
- 配置Node.js环境
- 项目初始化
- 安装body-parser、connect-multiparty、express框架
- 路由配置实现
- 页面实现
代码
//项目的文件入口,包含路由配置等。
var express = require('express');
//connect-multiparty用于解决文件上传问题
var mutipart = require('connect-multiparty');
var mutipartMiddeware = mutipart();
var app = express();
//将静态文件目录设置为"根目录+/public"
app.use(express.static('public'));
//修改临时文件储存位置
app.use(mutipart({
uploadDir: './public/uploadFolder'
}));
//设置HTTP服务监听的端口号
app.set('port', process.env.PORT || 3000);
app.listen(app.get('port'), function() {
console.log("Express started on localhost:" + app.get('port') + '; press Ctrl-C to terminate.');
});
//以下为路由配置
//1.当访问localhost:3000时。默认访问的页面
app.get('/', function(req, res) {
res.type('text/html');
res.sendFile(__dirname + '/HTML5Camera&Upload.html');
});
//2.当用户使用post方式提交表单到/upload服务时,读取表单内容并将文件上传
app.post('/upload', mutipartMiddeware, function(req, res) {
console.log(req.body.name + "\n=========\n");
//这里打印可以看到接收文件的信息
console.log(req.files.file.path + "\n=========\n");
var jsonObject = {};
//默认路径为:public\uploadFolder\aYm65V_E0Zn1ho5lDd,需要将public\部分去掉
var path = req.files.file.path;
path = path.substring(path.indexOf("\\"));
console.log(path);
jsonObject.image = path;
jsonObject.msg = "文件上传成功!"
//给浏览器返回成功提示
res.send(jsonObject);
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>拍照上传</title>
<link rel="stylesheet" type="text/css" href="styles/css/main.css" />
</head>
<body>
<div class="container">
<div class="header">
<div class="head-img">
<img id="uploadImage" src="styles/images/pdx2.jpeg">
<div class="button_border margin-t">编辑头像</div>
</div>
</div>
<div class="content">
<ul class="nav">
<li>我的主页</li>
<li><span class="selected">个人资料</span></li>
<li>相册</li>
<li>分享</li>
<li>日志</li>
<li>好友</li>
</ul>
<div class="position">
<span class="arrow"></span>当前所在位置:我的主页>>
<span class="green">个人资料</span>
</div>
<h3>修改头像</h3>
<form action="UploadServlet" method="post" enctype="multipart/form-data" id="uploadForm">
<div class="left">
<div class="gray_box">
<!-- <video id="video" autoplay="autoplay"></video> -->
<img src="styles/images/pdx1.jpeg" width="400" height="300">
</div>
<p><span>头像上传:</span>
<input type="file" id="headImage" onchange="changeHeadImage()" />
</p>
<p><span>头像描述:</span>
<textarea rows="3" cols="30"></textarea>
</p>
<div class="save">
<span class="button_border">取消</span>
<span class="button_blue" onclick="sendForm()">保存</span>
</div>
</div>
<div class="right">
<p>拍照效果:<br><br></p>
<div class="photo" id="box">
<canvas id="canvas" width="400" height="300"></canvas>
<div id="shade"></div>
</div>
<div id="">
<span id="reSnap" class="button_border">拍照</span>
</div>
拍照截图效果:<br>
<div class="cutPhoto">
<canvas id="cutCanvas" width="200" height="200"></canvas>
</div>
</div>
</form>
</div>
</div>
<div class="footer">学习之路,亦是孤独;愿君砥砺,求败独孤</div>
<script type="text/javascript">
//获得Canvas对象
var canvas = document.getElementById("canvas");
var cutCanvas = document.getElementById("cutCanvas");
var context = canvas.getContext("2d");
var cutContext = cutCanvas.getContext("2d");
//获得video摄像头区域
var video = document.getElementById("video");
var shade = document.getElementById("shade");
var box = document.getElementById("box");
var reSnap = document.getElementById("reSnap");
//阴影区域左上角的x,y坐标
var shadeX, shadeY;
//当DOM树构建完成时的时候就会执行DOMContentLoaded事件
window.addEventListener("DOMContentLoaded", function() {
//兼容主流浏览器
navigator.getMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator
.mozGetUserMedia || navigator.msGetUserMedia;
//从摄像头读取视频流,并在video中显示
navigator.getMedia({
video: true,
audio: false
},
//读取成功,对视频进行处理
function(stream) {
if (navigator.mozGetUserMedia) {
video.mozSrcObject = stream;
} else {
var vendorURL = window.URL || window.webkitURL;
video.src = vendorURL.createObjectURL(stream);
}
video.play();
},
//读取失败时所调用的回调函数
function(err) {
console.log("错误信息:" + err);
});
}, false);
//从视频中抓拍照片
reSnap.onclick = function() {
context.drawImage(video, 0, 0, 400, 300);
init();
shade.style.display = "none";
}
//绑定鼠标移动触发的事件
function init() {
box.onmouseout = function(ev) {
document.body.style.cursor = "";
}
box.onmousemove = function(ev) {
//设定鼠标样式
document.body.style.cursor = "move";
//获取box对象的左侧到浏览器窗口左侧的距离
var boxX = getLeft(box);
//获取box对象的顶部到浏览器窗口顶部的距离
var boxY = getTop(box);
//计算阴影区域的左上角x的坐标
shadeX = ev.pageX - boxX - 100;
//计算阴影区域的左上角y的坐标
shadeY = ev.pageY - boxY - 100;
//防止阴影区移到图片之外
if (shadeX < 0) {
shadeX = 0;
} else if (shadeX > 0) {
shadeX = 200;
}
if (shadeY < 0) {
shadeY = 0;
} else if (shadeY > 0) {
shadeY = 100;
}
shade.style.display = "block";
shade.style.left = shadeX + "px";
shade.style.top = shadeY + "px";
}
}
init();
box.onclick = function() {
//将box的onmosemove事件清空
box.onmousemove = function() {
};
cutContext.drawImage(canvas, shadeX, shadeY, 200, 200, 0, 0, 200, 200);
shadeX = 0;
shadeY = 0;
}
//获取元素的纵坐标(相对于body)
function getTop(e) {
var offset = e.offsetTop;
if (e.offsetParent != null) {
offset += getTop(e.offsetParent);
}
return offset;
}
//获取元素的横坐标(相对于body)
function getLeft(e) {
var offset = e.offsetLeft;
if (e.offsetParent != null) {
offset += getLeft(e.offsetParent);
}
return offset;
}
//用于提交表单
function sendForm() {
var data = cutCanvas.toDataURL();
data = data.split(',')[1];
data = window.atob(data);
var ia = new Uint8Array(data.length);
for (var i = 0; i < data.length; i++) {
ia[i] = data.charCodeAt(i);
}
//canvas.toDataURL返回的默认格式是image/png
var blob = new Blob([ia], {
type: "image/jpg"
});
//创建FormData对象,用于封装表单数据
var formData = new FormData(document.getElementById('uploadForm'));
formData.append('file', blob);
var xhr = new XMLHttpRequest();
//请求URL
xhr.open('POST', 'upload');
xhr.responseType = "json";
xhr.send(formData);
xhr.onload = function(event) {
if (xhr.status == 200) {
var json = xhr.response;
document.getElementById("uploadImage").src = json.image;
console.log(json.image);
context.fillStyle = "#FFFFFF";
context.fillRect(0, 0, canvas.width, canvas.height);
cutContext.fillStyle = "#ffffff";
cutContext.fillRect(0, 0, cutCanvas.width, cutCanvas.height);
shade.style.display = "none";
} else {
alert('保存出错了');
}
}
}
//用于加载本地图像文件
function changeHeadImage() {
var file = document.getElementById("headImage");
if (window.FileReader) {
var fr = new FileReader();
fr.onloadend = function(e) {
var image = new Image();
image.src = e.target.result;
image.onload = function() {
context.drawImage(image, 0, 0, 400, 300);
};
};
fr.readAsDataURL(file.files[0]);
}
}
</script>
</body>
</html>
结果
步骤详解
-
控制台使用npm init命令对项目初始化
-
控制台分别使用 npm install body-parse --save-dev 命令、npm install connect-multiparty --save-dev 命令来安装Express框架、npm install express --save-dev 命令来安装body-parse、connect-multiparty、express框架
-
第1,2步配置完成后package.json文件内容如下
-
目录结构
-
创建app.js作为项目文件入口,里面编写路由配置等信息
-
完成HTML5Camera&Upload.html页面编写
-
最后在控制台中使用 node app.js 命令启动Node.js服务器,在浏览器中输入 localhost:3000 网址进入测试。
PS
-
使用require()方法来加载express和connect-multiparty两个框架,其中express框架作为服务端的路由,根据用户的url请求分配到相应的应用程序
-
connect-multiparty用于实现用户文件上传功能
-
当用户以post方式访问"/upload/"路径请求时,服务端将对用户提交的头像和描述信息进行处理,并返回一个JSON数据。
-
运行过程中出现了参数类型报错,请注意查看FormData数据传递的类型是否正确。
-
运行过程中出现:400 (Bad Request)报错,请主义查看页面间提交的数据是否为同一类型。