最近项目需要做一个如下图所示的图片上传功能;
找了很多插件发现都不是我自己想要的,索性自己实现一个,也可以学习一下;
主要的思想就是:
1.所有的图片包括相机图标的蓝色窗口(以下皆描述为相机)都看成一个li,点击相机选择文件上传;
2.读取到文件之后,循环在相机之前插入图片的li,具体的图片处理,见2-1至2-5
3.如果图片个数达到要求,隐藏相机;
4.删除图片,将对应的li remove掉即可,注意相机的隐藏和展示;
图片的读取以及上传原理:
2-1.使用输入框的file类型,进行图片的获取
2-2.使用FileReader进行文件的读取,并且转换成dataURL格式
2-3.采用canvas对图片进行剪裁,并且转成dataURL格式;
2-4.将dataURL数据转成bolb格式
2-5.将bolb添加到FormData中
2-6.使用XMLHttpRequest进行图片上传,可以查看进度;
以上就是一个完整的实现流程,如果不需要进行剪裁图片,直接原图上传,直接省略第2-3点;
1.使用input的多文件上传,设置好accept
<input type="file" multiple onchange="selectImage(this)" accept="image/gif, image/jpeg, image/png" id="upload" >
2.使用FileReader读取文件
在这里遇到了一个问题,开始我在load中获取值是使用的reader.result,导致一直只能拿到最后一条数据;然后断点发现reader.load是异步执行的,在for循环结束之后才能顺序执行load方法,所以在load中获取url不可使用 reader.result,必须通过load函数的参数进行获取值
function selectImage(imgFile) {
var allFile = imgFile.files;
var imageArr = [];
for(var i=0;i<allFile.length;i++){
var file = allFile[i];
//添加一层过滤
var rFilter = /^(image\/bmp|image\/gif|image\/jpeg|image\/png|image\/tiff)$/i;
if(!rFilter.test(file.type)) {
alert("文件格式必须为图片");
return;
}
var reader = new FileReader();
reader.readAsDataURL(file); //用文件加载器加载文件
//文件加载完成
reader.onload = function(e) {
//计算最后一个窗口right边距,当时处于第4个的时候,right=0
if((allFile.length + 1)%4 == 0){
document.getElementById("uploadBtn").style.marginRight = "0px";
}
//以下就是将所有上传的图片回显到页面上,如果需要用canvas进行剪裁再回显以下代码就放入到canvas中
var li = document.createElement('li');
li.className = "upload-li";
li.innerHTML = '<div class="item image">'+
'<img class="upload-image" src="'+e.target.result+'"/>'+
'<img class="delete-image" src="assets/image/image-delete.png"/>'+
'</div>';
document.getElementById("uploadUL").insertBefore(li, document.getElementById("uploadBtn"));
};
}
}
3.如果需要对图像进行剪裁再回显,加载页面的代码应该放入到这一步,按照你的需求去完成;
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
img 规定要使用的图像、画布或视频。
sx 可选。开始剪切的 x 坐标位置。
sy 可选。开始剪切的 y 坐标位置。
swidth 可选。被剪切图像的宽度。
sheight 可选。被剪切图像的高度。
x 在画布上放置图像的 x 坐标位置。
y 在画布上放置图像的 y 坐标位置。
width 可选。要使用的图像的宽度。(伸展或缩小图像)
height 可选。要使用的图像的高度。(伸展或缩小图像)
按照你自己的需求去剪裁,也可以不进行剪裁,按照实际大小去展示;不剪裁的完整写法如下
function canvasImg(dataURL) {
const img = new window.Image();
img.src = dataURL;
var canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d");
img.onload = function() { //图片加载完成
//canvas 值按照你自己的实际需求去写
canvas.width = 131;
canvas.height = 131;
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
document.getElementById("uploadUL").insertBefore(canvas, document.getElementById("uploadBtn"));
};
}
将剪裁的图片转成dataURL格式
//图片的质量,这里设置的是1,也可以是小数
var quality = 1;
//获取画布图片,并且要jpg格式
var data = canvas.toDataURL("image/jpeg", quality);
data = data.split(',')[1];
4.将dataURL转成Blob
data = window.atob(data);
var ia = new Uint8Array(data.length);
for(var i = 0; i < data.length; i++) {
ia[i] = data.charCodeAt(i);
}
//以上均为二进制参数处理,从而获取一个blob对象
var resultBolb = new Blob([ia], {
type: "image/jpeg"
});
5.上传图片
function fileUpload(resultBolb) {
var fd = new FormData();
//向form中加入图片数据,name属性是file
fd.append("file", resultBolb);
//上传图片
var xhr = new XMLHttpRequest();
//请求成功
xhr.addEventListener('load', function(resUpload) {
}, false);
//请求失败
xhr.addEventListener('error', function() {
}, false);
//上传终止
xhr.addEventListener('abort', function() {
}, false);
//上传进度
xhr.upload.addEventListener('progress', function() {
}, false);
xhr.open('POST', "http://XXXXXXXXXXXXX"); //请求地址
xhr.send(fd); //发送
}
以上就是完成的js实现代码,以下简单展示一下实现的html+css:
<div class="upload-div">
<ul class="upload-ul" id="uploadUL">
<!--默认的点击窗口
在添加了图片之后,循环在这个前面insert图片的li
-->
<li class="upload-li" id="uploadBtn">
<form class="img-input-form" style="opacity: 0;">
<input type="file" multiple onchange="selectImage(this)" accept="image/gif, image/jpeg, image/png" id="upload" >
</form>
<div class="item">
<span class="photo-span"></span>
<span class="circle-span"></span>
<span class="circle-solid-span"></span>
</div>
</li>
</ul>
</div>
以上并不是完成代码,后面整理一下把完整代码放出来;欢迎提意见哦:)
很多朋友私信我到底怎么实现我下面把完整版代码贴出来;
以下代码经过了简化,和上面教程不太一样,主要是为了配合后台传数据,如果想按照上面教程进行做肯定也是没有问题的
需要导入jquery包+我自己写的一个公共方法包(需要的私信我邮箱或者评论留邮箱,我不贴出来了,太长了)
我们后台提供的接口是多图片上传的,所以就在选择了图片之后点击按钮调用fileUpload,按钮被我整理这个demo出来的时候删掉了,你们自己加一个按钮吧,这个也不难的;
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<title></title>
<link rel="shortcut icon" href="#" />
<meta id="viewport" name="viewport" content="width=750">
<meta http-equiv="Content-Type" content="multipart/form-data; charset=utf-8" />
<style>
html,body{
margin: 0;
padding: 0;
}
ul,li{
margin: 0;
padding:0;
}
.image-div{
padding: 30px 50px;
border-radius: 10px;
background-color: #fff;
margin: 40px 30px 0;
}
.image-div .title{
height: 70px;
line-height: 70px;
font-size: 32px;
color: #5f5f5f;
font-weight: 500;
}
.upload-div,
.show-div{
/*margin-top: 20px;*/
}
.upload-div .upload-ul,
.show-div .show-image-ul{
letter-spacing: -0.5em;
}
.upload-div .upload-ul .upload-li,
.show-div .show-image-ul .show-image-li{
height: 131px;
width: 131px;
margin-right: 22px;
letter-spacing: normal;
display: inline-block;
margin-top: 20px;
}
.show-div .show-image-ul .show-image-li img{
height: 100%;
width: 100%;
}
.upload-div .upload-ul .upload-li:nth-child(4n),
.show-div .show-image-ul .show-image-li:nth-child(4n){
margin-right: 0;
}
.upload-div .upload-ul .upload-li .item{
height: 100%;
width: 100%;
border-radius: 10px;
border: 3px dashed #97def1;
position: relative;
}
.upload-div .upload-ul .upload-li .item.image{
border: none;
font-size: 0;
}
.upload-div .upload-ul .upload-li .item .delete-image{
position: absolute;
height: 25px;
top: -12.5px;
left: -12.5px;
}
.upload-div .upload-ul .upload-li .item .upload-image{
height: 100%;
width: 100%;
border-radius: 10px;
vertical-align:initial;
}
.img-input-form{
position: absolute;
height: 131px;
width: 131px;
z-index: 999;
}
.img-input-form input{
position: absolute;
top: 0;
left: 0;
height: 131px;
width: 131px;
}
.photo-span{
display: inline-block;
position: absolute;
height: 32px;
width: 39px;
border-radius: 5px;
border: 3px solid #97def1;
left: 50%;
top: 50%;
margin-top: -16px;
margin-left: -19.5px;
}
.circle-span{
display: inline-block;
position: absolute;
height: 14px;
width: 14px;
border-radius: 7px;
border: 3px solid #97def1;
left: 50%;
top: 50%;
margin-top: -7px;
margin-left: -7px;
}
.circle-solid-span{
display: inline-block;
position: absolute;
height: 4px;
width: 4px;
border-radius: 2px;
background-color: #97def1;
left: 50%;
top: 50%;
margin-top: -10px;
margin-left: 9px;
}
.btm-btn{
height: 100px;
width: 500px;
border-radius: 50px;
text-align: center;
font-size: 26px;
color: #fff;
background-color: #ff7e00;
margin-left: 125px;
margin-top: 40px;
margin-bottom: 40px;
}
</style>
</head>
<body>
<div class="content" id="content">
<div class="image-div" id="uploadDiv">
<div class="title" id="imageTitle">
上传图片(最多4张)
</div>
<div class="upload-div" id="uploadImageDiv">
<ul class="upload-ul" id="uploadUL">
<!--默认的点击窗口
在添加了图片之后,循环在这个前面insert图片的li
-->
<li class="upload-li" id="uploadBtn">
<form class="img-input-form" enctype="multipart/form-data" style="opacity: 0;">
<input type="file" multiple onchange="selectImage(this)" accept="image/gif, image/jpeg, image/png" id="upload" >
</form>
<div class="item">
<span class="photo-span"></span>
<span class="circle-span"></span>
<span class="circle-solid-span"></span>
</div>
</li>
</ul>
</div>
</div>
</div>
<script src="assets/js/libs/jquery-2.1.1.min.js"></script>
<script src="assets/js/libs/yutil-1.0.1.js"></script>
<script type="text/javascript" charset="utf-8">
var uploadImgIndex = 0;
var imgArr = [];
function selectImage(imgFile){
var allFile = imgFile.files;
var totalLen = allFile.length;
if(yValidate.checkNotEmpty(imgArr) && imgArr.length>0){
totalLen = totalLen + imgArr.length;
}
if(totalLen>4){
alert("只能上传4张图片");
return;
}
for(var i=0;i<allFile.length;i++){
var file = allFile[i];
if(yValidate.checkNotEmpty(imgArr) && imgArr.length>0){
if(imgArr.length <4){
imgArr.push(file);
}
}else{
imgArr.push(file);
}
//添加一层过滤
var rFilter = /^(image\/bmp|image\/gif|image\/jpeg|image\/png|image\/tiff)$/i;
if(!rFilter.test(file.type)) {
alert("文件格式必须为图片");
return;
}
var reader = new FileReader();
reader.readAsDataURL(file); //用文件加载器加载文件
//文件加载完成
reader.onload = function(e) {
//计算最后一个窗口right边距,当时处于第4个的时候,right=0
if((allFile.length + 1)%4 == 0){
document.getElementById("uploadBtn").style.marginRight = "0px";
}
//以下就是将所有上传的图片回显到页面上,如果需要用canvas进行剪裁再回显以下代码就放入到canvas中
var li = document.createElement('li');
li.id = "upload_"+uploadImgIndex;
document.getElementById("uploadBtn").style.display = "";
uploadImgIndex++;
li.className = "upload-li";
li.innerHTML = '<div class="item image">'+
'<img class="upload-image" src="'+e.target.result+'"/>'+
'<img class="delete-image" src="assets/image/image-delete.png"/>'+
'</div>';
document.getElementById("uploadUL").insertBefore(li, document.getElementById("uploadBtn"));
var uploadArr = document.getElementById("uploadUL").querySelectorAll("li");
var len = uploadArr.length ;
if(len > 4){
document.getElementById("uploadBtn").style.display = "none";
}
};
reader.onloadend = function(e) {
$(".delete-image").off('click');
$(".delete-image").on('click',function(){
// alert("dasd");
var li = $(this).parent().parent()[0];
var index = $(".upload-ul .upload-li").index(li);
var liId = li.id;
$("#"+liId).remove();
imgArr.splice(index,1);
document.getElementById("uploadBtn").style.display = "";
});
}
}
}
function fileUpload(){
var param = new FormData();
for(var i=0; i<imgArr.length;i++){
param.append('file[]', imgArr[i], i);
}
param.append("orderId", req.id);
param.append("userId", bxUserData.id);
$("body").mLoading("show");
$.ajax({
url:url,
type:'POST',
data:param,
async: false,
cache: false,
contentType: false,
processData: false,
success:function(data){
//请求成功
},
error:function(){
//请求失败
alert(res.description || res.message || "上传失败");
}
});
}
</script>
</body>
</html>
---------------------------------------190102更新------------------------------------
代码放在了git,地址:https://github.com/super-jingjing/multipleUploadImage.git
不用给我留邮箱了哈,一直发邮件感觉有点麻烦